627 lines
52 KiB
MQL5
627 lines
52 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| CurrencyIndex.mqh |
|
|
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
|
|
#define SECONDS_IN_DAY (24*60*60) // количество секунд в сутках
|
|
#define SECONDS_IN_MINUTE 60 // количество секунд в минуте
|
|
#define MSECS_IN_MINIUTE (60*1000) // количество миллисекунд в минуте
|
|
|
|
//--- структура символа корзины
|
|
struct SymbolWeight
|
|
{
|
|
string symbol; // символ
|
|
double weight; // вес
|
|
};
|
|
|
|
//--- структура исторических данных
|
|
struct str_rates
|
|
{
|
|
int index; // индекс данных
|
|
MqlRates rates[]; // массив исторических данных
|
|
};
|
|
|
|
//--- структура тиковых данных
|
|
struct str_ticks
|
|
{
|
|
int index; // индекс данных
|
|
MqlTick ticks[]; // массив тиков
|
|
};
|
|
|
|
//--- перечисление типов цен
|
|
enum ENUM_RATES_VALUES
|
|
{
|
|
VALUE_OPEN, // цена Open
|
|
VALUE_HIGH, // цена High
|
|
VALUE_LOW, // цена Low
|
|
VALUE_CLOSE // цена Close
|
|
};
|
|
|
|
int ExtDigits=5; // точность измерения цены символа
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Обработка тиков |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessTick(const string custom_symbol)
|
|
{
|
|
static long last_time_msc=0; // время в миллисекундах последнего тика
|
|
static MqlTick synth_tick[1]; // структура последнего тика синтетического инструмента
|
|
static MqlTick ticks[BASKET_SIZE]; // массив данных последних тиков корзины символов
|
|
static MqlTick tick; // вспомогательная переменная для получения данных и поиска времени
|
|
int success_cnt=0;
|
|
int change_cnt=0;
|
|
|
|
//--- инициализируем время тика синтетического символа
|
|
synth_tick[0].time=0;
|
|
|
|
//--- в цикле по количеству символов в корзине инструментов
|
|
for(int i=0; i<BASKET_SIZE; i++)
|
|
{
|
|
//--- получаем данные очередного символа
|
|
if(SymbolInfoTick(ExtWeights[i].symbol,tick))
|
|
{
|
|
//--- увеличиваем количество успешных запросов данных
|
|
success_cnt++;
|
|
//--- получаем самое свежее время из списка символов корзины
|
|
if(synth_tick[0].time==0)
|
|
{
|
|
synth_tick[0].time=tick.time;
|
|
synth_tick[0].time_msc=tick.time_msc;
|
|
}
|
|
else
|
|
{
|
|
if(synth_tick[0].time_msc<tick.time_msc)
|
|
{
|
|
synth_tick[0].time=tick.time;
|
|
synth_tick[0].time_msc=tick.time_msc;
|
|
}
|
|
}
|
|
//--- сохраняем полученные данные по символу в массиве ticks в соответствии с индексом символа корзины
|
|
ticks[i]=tick;
|
|
}
|
|
}
|
|
//--- если получены тики всех инструментов корзины, и это новый тик
|
|
if(success_cnt==BASKET_SIZE && synth_tick[0].time!=0 && last_time_msc<synth_tick[0].time_msc)
|
|
{
|
|
//--- сохраняем время последнего тика
|
|
last_time_msc=synth_tick[0].time_msc;
|
|
|
|
//--- рассчитываем значение цены Bid синтетического инструмента
|
|
synth_tick[0].bid=MAIN_COEFF;
|
|
for(int i=0; i<BASKET_SIZE; i++)
|
|
synth_tick[0].bid*=MathPow(ticks[i].bid,ExtWeights[i].weight);
|
|
|
|
//--- цена Ask равна цене Bid
|
|
synth_tick[0].ask=synth_tick[0].bid;
|
|
|
|
//--- добавляем в ценовую историю пользовательского инструмента новый тик
|
|
CustomTicksAdd(custom_symbol,synth_tick);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Инициализация сервиса |
|
|
//+------------------------------------------------------------------+
|
|
bool InitService(const string custom_symbol,const string custom_group)
|
|
{
|
|
MqlRates rates[100];
|
|
MqlTick ticks[100];
|
|
|
|
//--- инициализируем пользовательский символ
|
|
if(!CustomSymbolInitialize(custom_symbol,custom_group))
|
|
return(false);
|
|
ExtDigits=(int)SymbolInfoInteger(custom_symbol,SYMBOL_DIGITS);
|
|
|
|
//--- делаем активными все символы корзины инструментов, участвующие в расчёте индекса
|
|
for(int i=0; i<BASKET_SIZE; i++)
|
|
{
|
|
//--- выбираем символ в окне "Обзор рынка"
|
|
if(!SymbolSelect(ExtWeights[i].symbol,true))
|
|
{
|
|
PrintFormat("cannot select symbol %s",ExtWeights[i].symbol);
|
|
return(false);
|
|
}
|
|
//--- запрашиваем исторические данные баров и тиков по выбранному символу
|
|
CopyRates(ExtWeights[i].symbol,PERIOD_M1,0,100,rates);
|
|
CopyTicks(ExtWeights[i].symbol,ticks,COPY_TICKS_ALL,0,100);
|
|
}
|
|
|
|
//--- строим M1 бары за 1 месяц
|
|
if(!PrepareRates(custom_symbol))
|
|
return(false);
|
|
|
|
//--- получаем последние тики после построения M1 баров
|
|
PrepareLastTicks(custom_symbol);
|
|
|
|
//--- сервис инициализирован
|
|
Print(custom_symbol," datafeed started");
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Инициализация пользовательского символа |
|
|
//+------------------------------------------------------------------+
|
|
bool CustomSymbolInitialize(string symbol,string group)
|
|
{
|
|
bool is_custom=false;
|
|
//--- если символ выбран в окне "Обзор рынка", получаем флаг, что это пользовательский символ
|
|
bool res=SymbolSelect(symbol,true);
|
|
if(res)
|
|
is_custom=(bool)SymbolInfoInteger(symbol,SYMBOL_CUSTOM);
|
|
|
|
//--- если выбранный символ не пользовательский - создаём его
|
|
if(!res)
|
|
{
|
|
if(!CustomSymbolCreate(symbol,group,"EURUSD"))
|
|
{
|
|
Print("cannot create custom symbol ",symbol);
|
|
return(false);
|
|
}
|
|
//--- символ успешно создан - устанавливаем флаг, что это пользовательский символ
|
|
is_custom=true;
|
|
|
|
//--- помещаем созданный символ в окно "Обзор рынка"
|
|
if(!SymbolSelect(symbol,true))
|
|
{
|
|
Print("cannot select custom symbol ",symbol);
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
//--- откроем график созданного пользовательского символа
|
|
if(is_custom)
|
|
{
|
|
//--- получаем идентификатор первого окна открытых графиков
|
|
long chart_id=ChartFirst();
|
|
bool found=false;
|
|
//--- в цикле по списку открытых графиков найдём график созданного пользовательского символа
|
|
while(chart_id>=0)
|
|
{
|
|
//--- если график открыт - сообщаем об этом в журнал, ставим флаг найденного графика и выходим из цикла поиска
|
|
if(ChartSymbol(chart_id)==symbol)
|
|
{
|
|
found=true;
|
|
Print(symbol," chart found");
|
|
break;
|
|
}
|
|
//--- на основании текущего выбранного графика получаем идентификатор следующего для очередной итерации поиска в цикле
|
|
chart_id=ChartNext(chart_id);
|
|
}
|
|
|
|
//--- если график символа не найден среди открытых графиков
|
|
if(!found)
|
|
{
|
|
//--- сообщаем об открытии графика M1 пользовательского символа,
|
|
//--- получаем идентификатор открываемого графика и переходим на него
|
|
Print("open chart ",symbol,",M1");
|
|
chart_id=ChartOpen(symbol,PERIOD_M1);
|
|
ChartSetInteger(chart_id,CHART_BRING_TO_TOP,true);
|
|
}
|
|
}
|
|
//--- пользовательский символ инициализирован
|
|
return(is_custom);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Подготовка исторических данных |
|
|
//+------------------------------------------------------------------+
|
|
bool PrepareRates(const string custom_symbol)
|
|
{
|
|
str_rates symbols_rates[BASKET_SIZE];
|
|
int i,reserve=0;
|
|
MqlRates usdx_rates[]; // массив-таймсерия синтетического инструмента
|
|
MqlRates rate; // данные одного бара синтетического инструмента
|
|
datetime stop=(TimeCurrent()/SECONDS_IN_MINUTE)*SECONDS_IN_MINUTE; // время бара M1 конечной даты
|
|
datetime start=stop-31*SECONDS_IN_DAY; // время бара M1 начальной даты
|
|
datetime start_date=0;
|
|
|
|
//--- копируем исторические данные M1 за месяц для всех символов корзины инструментов
|
|
start/=SECONDS_IN_DAY;
|
|
start*=SECONDS_IN_DAY; // время бара D1 начальной даты
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
{
|
|
if(CopyRates(ExtWeights[i].symbol,PERIOD_M1,start,stop,symbols_rates[i].rates)<=0)
|
|
{
|
|
PrintFormat("cannot copy rates for %s,M1 from %s to %s [%d]",ExtWeights[i].symbol,TimeToString(start),TimeToString(stop),GetLastError());
|
|
return(false);
|
|
}
|
|
PrintFormat("%u %s,M1 rates from %s",ArraySize(symbols_rates[i].rates),ExtWeights[i].symbol,TimeToString(symbols_rates[i].rates[0].time));
|
|
symbols_rates[i].index=0;
|
|
//--- находим и устанавливаем минимальную ненулевую начальную дату из корзины символов
|
|
if(start_date<symbols_rates[i].rates[0].time)
|
|
start_date=symbols_rates[i].rates[0].time;
|
|
}
|
|
Print("start date set to ",start_date);
|
|
|
|
//--- резерв массива исторических данных для избежания перераспределения памяти при изменении размера массива
|
|
reserve=int(stop-start)/60;
|
|
|
|
//--- установим начало всех исторических данных корзины символов на одну дату (start_date)
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
{
|
|
int j=0;
|
|
//--- до тех пор, пока j меньше количества данных в массиве rates и
|
|
//--- время по индексу j в массиве меньше времени start_date - увеличиваем индекс
|
|
while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<start_date)
|
|
j++;
|
|
//--- если индекс был увеличен, и он в пределах массива rates - уменьшим его на 1 для компенсации последнего приращения
|
|
if(j>0 && j<ArraySize(symbols_rates[i].rates))
|
|
j--;
|
|
//--- запишем полученный индекс в структуру
|
|
symbols_rates[i].index=j;
|
|
}
|
|
|
|
//--- таймсерии USD index
|
|
int array_size=0;
|
|
|
|
//--- первый бар таймсерии M1
|
|
rate.time=start_date;
|
|
rate.real_volume=0;
|
|
rate.spread=0;
|
|
|
|
//--- до тех пор, пока время бара меньше времени конечной даты таймсерии M1
|
|
while(!IsStopped() && rate.time<stop)
|
|
{
|
|
//--- если исторические данные бара инструмента рассчитаны
|
|
if(CalculateRate(rate,symbols_rates))
|
|
{
|
|
//--- увеличиваем массив-таймсерию на 1 и добавляем в него рассчитанные данные
|
|
ArrayResize(usdx_rates,array_size+1,reserve);
|
|
usdx_rates[array_size]=rate;
|
|
array_size++;
|
|
//--- сбросим размер резервного значения размера массива, так как он применяется только при первом изменении размера
|
|
reserve=0;
|
|
}
|
|
|
|
//--- следующий бар таймсерии M1
|
|
rate.time+=PeriodSeconds(PERIOD_M1);
|
|
start_date=rate.time;
|
|
|
|
//--- в цикле по списку инструментов корзины
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
{
|
|
//--- получаем текущий индекс данных
|
|
int j=symbols_rates[i].index;
|
|
//--- пока j в пределах данных таймсерии и, если время бара по индексу j меньше времени, установленного для этого бара в rate.time, увеличиваем индекс j
|
|
while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<rate.time)
|
|
j++;
|
|
//--- если j в пределах данных таймсерии и время в start_date меньше времени данных таймсерии по индексу j
|
|
//--- и время в таймсерии по индексу j меньше, либо равно времени в rate.time - записывавем в start_date время из таймсерии по индексу j
|
|
if(j<ArraySize(symbols_rates[i].rates) && start_date<symbols_rates[i].rates[j].time && symbols_rates[i].rates[j].time<=rate.time)
|
|
start_date=symbols_rates[i].rates[j].time;
|
|
}
|
|
|
|
//--- в цикле по списку инструментов корзины
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
{
|
|
//--- получаем текущий индекс данных
|
|
int j=symbols_rates[i].index;
|
|
//--- пока j в пределах данных таймсерии и, если время бара по индексу j меньше времени, установленного для этого бара в start_date, увеличиваем индекс j
|
|
while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<=start_date)
|
|
symbols_rates[i].index=j++;
|
|
}
|
|
//--- в rate.time запишем время из start_date для последующего бара
|
|
rate.time=start_date;
|
|
}
|
|
|
|
//--- добавляем в базу созданную таймсерию
|
|
if(array_size>0)
|
|
{
|
|
if(!IsStopped())
|
|
{
|
|
int cnt=CustomRatesReplace(custom_symbol,usdx_rates[0].time,usdx_rates[ArraySize(usdx_rates)-1].time+1,usdx_rates);
|
|
Print(cnt," ",custom_symbol,",M1 rates from ",usdx_rates[0].time," to ",usdx_rates[ArraySize(usdx_rates)-1].time," added");
|
|
}
|
|
}
|
|
//--- успешно
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Подготовка последних тиков |
|
|
//+------------------------------------------------------------------+
|
|
void PrepareLastTicks(const string custom_symbol)
|
|
{
|
|
str_ticks symbols_ticks[BASKET_SIZE];
|
|
int i,j,cnt,reserve=0;
|
|
MqlTick usdx_ticks[]; // массив тиков синтетического инструмента
|
|
MqlTick tick={0}; // данные одного тика синтетического инструмента
|
|
|
|
long time_to=TimeCurrent()*1000; // время конца тиковых данных в миллисекундах
|
|
long start_date=(time_to/MSECS_IN_MINIUTE)*MSECS_IN_MINIUTE; // время открытия бара в миллисекундах со временем TimeCurrent()
|
|
long time_from=start_date-MSECS_IN_MINIUTE; // время начала копирования тиковых данных в миллисекундах
|
|
|
|
//--- если были тики за последнюю минуту
|
|
if(SymbolInfoTick(custom_symbol,tick) && tick.time_msc>=start_date)
|
|
{
|
|
Print(custom_symbol," last tick at ",datetime(tick.time_msc/1000),":",IntegerToString(tick.time_msc%1000,3,'0'));
|
|
str_rates symbols_rates[BASKET_SIZE];
|
|
bool copy_error=false;
|
|
|
|
//--- в цикле по количеству символов в корзине инструментов
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
{
|
|
//--- копируем два последних бара исторических данных инструмента
|
|
if(CopyRates(ExtWeights[i].symbol,PERIOD_M1,0,2,symbols_rates[i].rates)!=2)
|
|
{
|
|
Print("cannot copy ",ExtWeights[i].symbol," rates [",GetLastError(),"]");
|
|
copy_error=true;
|
|
break;
|
|
}
|
|
symbols_rates[i].index=1;
|
|
}
|
|
|
|
//--- рассчитываем данные последней минуты
|
|
if(!copy_error)
|
|
{
|
|
MqlRates rate;
|
|
double values[BASKET_SIZE]={0};
|
|
rate.time=datetime(start_date/1000);
|
|
rate.real_volume=0;
|
|
rate.spread=0;
|
|
|
|
//--- если исторические данные бара инструмента рассчитаны
|
|
if(CalculateRate(rate,symbols_rates))
|
|
{
|
|
MqlRates usdx_rates[1];
|
|
|
|
//--- заменяем рассчитанными данными бара историю последнего бара M1 пользовательского инструмента
|
|
usdx_rates[0]=rate;
|
|
cnt=CustomRatesUpdate(custom_symbol,usdx_rates);
|
|
if(cnt==1)
|
|
{
|
|
Print(custom_symbol,",M1 last minute rate ",rate.time," added");
|
|
//--- время в миллисекундах последующих добавляемых тииков
|
|
start_date=tick.time_msc+1;
|
|
}
|
|
}
|
|
else
|
|
Print(custom_symbol,",M1 last minute rate ",rate.time," ",rate.open," ",rate.high," ",rate.low," ",rate.close," not updated");
|
|
}
|
|
}
|
|
|
|
//--- получаем все тики с начала предыдущей минуты
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
{
|
|
if(CopyTicksRange(ExtWeights[i].symbol,symbols_ticks[i].ticks,COPY_TICKS_ALL,time_from,time_to)<=0)
|
|
{
|
|
PrintFormat("cannot copy ticks for %s",ExtWeights[i].symbol);
|
|
return;
|
|
}
|
|
PrintFormat("%u %s ticks from %s",ArraySize(symbols_ticks[i].ticks),ExtWeights[i].symbol,TimeToString(symbols_ticks[i].ticks[0].time,TIME_DATE|TIME_SECONDS));
|
|
symbols_ticks[i].index=0;
|
|
}
|
|
|
|
//--- резерв массива тиков для избегания перераспределения памяти при изменении размера
|
|
reserve=ArraySize(symbols_ticks[0].ticks);
|
|
|
|
//--- установим начало всех тиков на одну дату start_date
|
|
j=0;
|
|
while(j<ArraySize(symbols_ticks[0].ticks) && symbols_ticks[0].ticks[j].time_msc<start_date)
|
|
j++;
|
|
if(j>=ArraySize(symbols_ticks[0].ticks))
|
|
{
|
|
Print("no ticks at ",datetime(start_date/1000),":",IntegerToString(start_date%1000,3,'0')," (",start_date/1000,")" );
|
|
return;
|
|
}
|
|
symbols_ticks[0].index=j;
|
|
long time_msc=symbols_ticks[0].ticks[j].time_msc;
|
|
for(i=1; i<BASKET_SIZE; i++)
|
|
{
|
|
j=0;
|
|
while(j<ArraySize(symbols_ticks[i].ticks) && symbols_ticks[i].ticks[j].time_msc<time_msc)
|
|
j++;
|
|
if(j>0 && j<ArraySize(symbols_ticks[i].ticks))
|
|
j--;
|
|
symbols_ticks[i].index=j;
|
|
}
|
|
|
|
//--- тики USD index
|
|
double values[BASKET_SIZE]={0};
|
|
int array_size=0;
|
|
|
|
//--- первый тик
|
|
tick.last=0;
|
|
tick.volume=0;
|
|
tick.flags=0;
|
|
|
|
//--- в цикле от индекса j (от начальной даты всех тиков корзины инструментов)
|
|
//--- по количеству полученных тиков первого инструмента корзины
|
|
for(j=symbols_ticks[0].index; j<ArraySize(symbols_ticks[0].ticks); j++)
|
|
{
|
|
//--- записываем данные тика по индексу цикла j
|
|
tick.time=symbols_ticks[0].ticks[j].time; // время тика
|
|
tick.time_msc=symbols_ticks[0].ticks[j].time_msc; // время тика в миллисекундах
|
|
|
|
//--- рассчитаем значение цены Bid по весам всех символов корзины инструментов
|
|
values[0]=symbols_ticks[0].ticks[j].bid;
|
|
symbols_ticks[0].index++;
|
|
for(i=1; i<BASKET_SIZE; i++)
|
|
values[i]=GetTickValue(symbols_ticks[i],symbols_ticks[0].ticks[j].time_msc);
|
|
tick.bid=MAIN_COEFF;
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
tick.bid*=MathPow(values[i],ExtWeights[i].weight);
|
|
//--- цена Ask равна рассчитанной цене Bid инструмента
|
|
tick.ask=tick.bid;
|
|
|
|
//--- добавляем рассчитанный тик в массив тиков синтетического инструмента
|
|
ArrayResize(usdx_ticks,array_size+1,reserve);
|
|
usdx_ticks[array_size]=tick;
|
|
array_size++;
|
|
|
|
//--- обнуляем размер резервированной памяти, так как он нужен только при первом ArrayResize
|
|
reserve=0;
|
|
}
|
|
|
|
//--- Добавляем в ценовую историю пользовательского инструмента данные из собранного массива тиков
|
|
if(array_size>0)
|
|
{
|
|
Print(array_size," ticks from ",usdx_ticks[0].time,":",IntegerToString(usdx_ticks[0].time_msc%1000,3,'0')," prepared");
|
|
cnt=CustomTicksAdd(custom_symbol,usdx_ticks);
|
|
if(cnt>0)
|
|
Print(cnt," ticks applied");
|
|
else
|
|
Print("no ticks applied");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Расчёт цен и объёмов синтетического инструмента |
|
|
//+------------------------------------------------------------------+
|
|
bool CalculateRate(MqlRates& rate,str_rates& symbols_rates[])
|
|
{
|
|
double values[BASKET_SIZE]={0};
|
|
long tick_volume=0;
|
|
int i;
|
|
//--- получаем цены Open всех символов корзины инструментов в массив values[]
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_OPEN);
|
|
|
|
//--- если тиковый объём нулевой, значит нет данных на этой минуте - возвращаем false
|
|
if(tick_volume==0)
|
|
return(false);
|
|
|
|
//--- запишем совокупный объем всех таймсерий
|
|
rate.tick_volume=tick_volume;
|
|
|
|
//--- рассчитаем цену Open по ценам и весам всех инструментов корзины
|
|
rate.open=MAIN_COEFF;
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
rate.open*=MathPow(values[i],ExtWeights[i].weight);
|
|
|
|
//--- рассчитаем цену High по ценам и весам всех инструментов корзины
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_HIGH);
|
|
rate.high=MAIN_COEFF;
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
rate.high*=MathPow(values[i],ExtWeights[i].weight);
|
|
|
|
//--- рассчитаем цену Low по ценам и весам всех инструментов корзины
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_LOW);
|
|
rate.low=MAIN_COEFF;
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
rate.low*=MathPow(values[i],ExtWeights[i].weight);
|
|
|
|
//--- рассчитаем цену Close по ценам и весам всех инструментов корзины
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_CLOSE);
|
|
rate.close=MAIN_COEFF;
|
|
for(i=0; i<BASKET_SIZE; i++)
|
|
rate.close*=MathPow(values[i],ExtWeights[i].weight);
|
|
|
|
//--- возвращаем результат проверки цен на корректность
|
|
return(CheckRate(rate));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает указанную цену бара |
|
|
//+------------------------------------------------------------------+
|
|
double GetRateValue(long &tick_volume,str_rates &symbol_rates,datetime time,ENUM_RATES_VALUES num_value)
|
|
{
|
|
double value=0; // получаемое значение
|
|
int index=symbol_rates.index; // индекс данных
|
|
|
|
//--- если индекс в пределах таймсерии
|
|
if(index<ArraySize(symbol_rates.rates))
|
|
{
|
|
//--- в зависимости от типа запрашиваемых данных записываем соответствующее значение в переменную value
|
|
switch(num_value)
|
|
{
|
|
//--- цена Open
|
|
case VALUE_OPEN:
|
|
if(symbol_rates.rates[index].time<time)
|
|
value=symbol_rates.rates[index].close;
|
|
else
|
|
{
|
|
if(symbol_rates.rates[index].time==time)
|
|
{
|
|
value=symbol_rates.rates[index].open;
|
|
//--- при запросе цены Open добавляем тиковый объём к переменной tick_volume, передаваемой по ссылке,
|
|
//--- для получения суммарного объёма всех символов корзины инструментов
|
|
tick_volume+=symbol_rates.rates[index].tick_volume;
|
|
}
|
|
}
|
|
break;
|
|
//--- цена High
|
|
case VALUE_HIGH:
|
|
if(symbol_rates.rates[index].time<time)
|
|
value=symbol_rates.rates[index].close;
|
|
else
|
|
{
|
|
if(symbol_rates.rates[index].time==time)
|
|
value=symbol_rates.rates[index].high;
|
|
}
|
|
break;
|
|
//--- цена Low
|
|
case VALUE_LOW:
|
|
if(symbol_rates.rates[index].time<time)
|
|
value=symbol_rates.rates[index].close;
|
|
else
|
|
{
|
|
if(symbol_rates.rates[index].time==time)
|
|
value=symbol_rates.rates[index].low;
|
|
}
|
|
break;
|
|
//--- цена Close
|
|
case VALUE_CLOSE:
|
|
if(symbol_rates.rates[index].time<=time)
|
|
value=symbol_rates.rates[index].close;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//--- возвращаем полученное значение
|
|
return(value);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Проверка цен на корректность и возврат результата проверки |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckRate(MqlRates &rate)
|
|
{
|
|
//--- если цены представляют собой не корректные действительные числа, или меньше, либо равны нулю - возвращаем false
|
|
if(!MathIsValidNumber(rate.open) || !MathIsValidNumber(rate.high) || !MathIsValidNumber(rate.low) || !MathIsValidNumber(rate.close))
|
|
return(false);
|
|
if(rate.open<=0.0 || rate.high<=0.0 || rate.low<=0.0 || rate.close<=0.0)
|
|
return(false);
|
|
|
|
//--- нормализуем цены до требуемого количества знаков
|
|
rate.open=NormalizeDouble(rate.open,ExtDigits);
|
|
rate.high=NormalizeDouble(rate.high,ExtDigits);
|
|
rate.low=NormalizeDouble(rate.low,ExtDigits);
|
|
rate.close=NormalizeDouble(rate.close,ExtDigits);
|
|
|
|
//--- корректируем при необходимости цены
|
|
if(rate.high<rate.open)
|
|
rate.high=rate.open;
|
|
if(rate.low>rate.open)
|
|
rate.low=rate.open;
|
|
if(rate.high<rate.close)
|
|
rate.high=rate.close;
|
|
if(rate.low>rate.close)
|
|
rate.low=rate.close;
|
|
|
|
//--- всё успешно
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает значение тика |
|
|
//+------------------------------------------------------------------+
|
|
double GetTickValue(str_ticks &symbol_ticks,long time_msc)
|
|
{
|
|
double value=0;
|
|
//--- если индекс данных, записанный в структуре symbol_ticks, находится в пределах массива тиков структуры
|
|
if(symbol_ticks.index<ArraySize(symbol_ticks.ticks))
|
|
{
|
|
//--- получаем значение цены Bid из структуры по индексу данных
|
|
value=symbol_ticks.ticks[symbol_ticks.index].bid;
|
|
//--- если время в структуре в миллисекундах по индексу в структуре меньше переданного в функцию времени
|
|
if(symbol_ticks.ticks[symbol_ticks.index].time_msc<time_msc)
|
|
{
|
|
//--- до тех пор, пока индекс находится в пределах таймсерии в структуре и
|
|
//--- если время в структуре меньше переданного в функцию времени - увеличиваем индекс
|
|
while(symbol_ticks.index<ArraySize(symbol_ticks.ticks) && symbol_ticks.ticks[symbol_ticks.index].time_msc<time_msc)
|
|
symbol_ticks.index++;
|
|
}
|
|
}
|
|
//--- возвращаем полученное значение
|
|
return(value);
|
|
}
|
|
//+------------------------------------------------------------------+
|