145 行
12 KiB
MQL5
145 行
12 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| FVE.mq5 |
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2026, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
#property description "Finite Volume Elements"
|
|
#property indicator_separate_window
|
|
#property indicator_buffers 3
|
|
#property indicator_plots 1
|
|
//--- plot FVE
|
|
#property indicator_label1 "FVE"
|
|
#property indicator_type1 DRAW_LINE
|
|
#property indicator_color1 clrRed
|
|
#property indicator_style1 STYLE_SOLID
|
|
#property indicator_width1 1
|
|
|
|
enum ENUM_USED_VOLUME // Перечисление используемых объёмов
|
|
{
|
|
USED_VOLUME_REAL, // Real Volume
|
|
USED_VOLUME_TICK, // Tick Volume
|
|
};
|
|
|
|
//--- input parameters
|
|
input(name="Samples") int InpSamples = 22; // Период расчёта
|
|
input(name="Threshold") double InpCutOff = 0.3; // Порог чувствительности
|
|
input(name="Used Volume") ENUM_USED_VOLUME InpUsedVolume = USED_VOLUME_TICK; // Используемый объём
|
|
|
|
//--- indicator buffers
|
|
double BufferFVE[];
|
|
double BufferVolumePlusMinus[];
|
|
double BufferVolumes[];
|
|
|
|
//--- global variables
|
|
int samples;
|
|
double cutoff;
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
//--- indicator buffers mapping
|
|
SetIndexBuffer(0,BufferFVE,INDICATOR_DATA);
|
|
SetIndexBuffer(1,BufferVolumePlusMinus,INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(2,BufferVolumes,INDICATOR_CALCULATIONS);
|
|
|
|
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
|
|
|
|
//--- Проверка и установка периода и порога
|
|
samples=(InpSamples<1? 22 : InpSamples);
|
|
cutoff=InpCutOff/100.0;
|
|
//--- Установка имени индикатора и уровня 0
|
|
IndicatorSetString(INDICATOR_SHORTNAME,StringFormat("FVE(%d,%.3f)",samples,InpCutOff));
|
|
IndicatorSetInteger(INDICATOR_LEVELS,1);
|
|
IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.0);
|
|
|
|
ArraySetAsSeries(BufferFVE,true);
|
|
ArraySetAsSeries(BufferVolumePlusMinus,true);
|
|
ArraySetAsSeries(BufferVolumes,true);
|
|
|
|
//--- Всё успешно
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator iteration function |
|
|
//+------------------------------------------------------------------+
|
|
int OnCalculate(const int32_t rates_total,
|
|
const int32_t prev_calculated,
|
|
const datetime &time[],
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[],
|
|
const long &tick_volume[],
|
|
const long &volume[],
|
|
const int32_t &spread[])
|
|
{
|
|
//--- Проверка количества доступных баров
|
|
if(rates_total<samples+1)
|
|
return(0);
|
|
|
|
//--- Массивы для расчёта - как таймсерии
|
|
ArraySetAsSeries(high,true);
|
|
ArraySetAsSeries(low,true);
|
|
ArraySetAsSeries(close,true);
|
|
ArraySetAsSeries(volume,true);
|
|
ArraySetAsSeries(tick_volume,true);
|
|
|
|
//--- Проверка и расчёт количества просчитываемых баров
|
|
int limit=rates_total-prev_calculated;
|
|
|
|
//--- Если очередной новый тик - ничего не делаем
|
|
if(limit==0)
|
|
return(rates_total);
|
|
|
|
//--- Если первый запуск или изменения исторических данных
|
|
if(limit>1)
|
|
{
|
|
//--- расчёт начинаем от начала исторических данных,
|
|
//--- инициализируем буферы индикатора нулевыми значениями
|
|
limit=rates_total-samples-1;
|
|
ArrayInitialize(BufferFVE,0);
|
|
ArrayInitialize(BufferVolumePlusMinus,0);
|
|
ArrayInitialize(BufferVolumes,0);
|
|
}
|
|
//--- Расчёт индикатора (либо вся история, либо каждый очередной новый бар)
|
|
for(int i=limit; i>=0; i--)
|
|
{
|
|
//--- Типичные цены для текущего и предыдущего баров
|
|
double TP_curr=(high[i]+low[i]+close[i])/3.0;
|
|
double TP_prev=(high[i+1]+low[i+1]+close[i+1])/3.0;
|
|
|
|
//--- Рассчитываем текущие метрику и направление движения цены
|
|
double MF=(close[i]-(high[i]+low[i])/2.0)+TP_curr-TP_prev;
|
|
int FveFactor=(MF>cutoff*close[i]) ? 1 : (MF< -cutoff*close[i]) ? -1 : 0;
|
|
|
|
//--- Записываем текущие скорректированный и общий объёмы в буферы
|
|
long vol=Volume(i,volume,tick_volume);
|
|
BufferVolumePlusMinus[i]=double(vol*FveFactor);
|
|
BufferVolumes[i]=(double)vol;
|
|
|
|
//--- Суммируем скорректированный и общий объём за samples баров
|
|
double FVEsum=0, VolSum=0;
|
|
for(int j=0;j<samples;j++)
|
|
{
|
|
int idx=i+j;
|
|
FVEsum+=BufferVolumePlusMinus[idx]; // Сумма скорректированного объёма за samples баров
|
|
VolSum+=BufferVolumes[idx]; // Сумма общего объёма за samples баров
|
|
}
|
|
//--- Рассчитываем FVE
|
|
BufferFVE[i]=(VolSum!=0 ? (FVEsum/VolSum)*100.0 : 0.0);
|
|
}
|
|
//--- return value of prev_calculated for next call
|
|
return(rates_total);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает объём в зависимости от выбранного в настройках |
|
|
//+------------------------------------------------------------------+
|
|
long Volume(const int index,const long &volume_real[],const long &volume_tick[])
|
|
{
|
|
return(InpUsedVolume==USED_VOLUME_REAL ? volume_real[index] : volume_tick[index]);
|
|
}
|
|
//+------------------------------------------------------------------+
|