Можно использовать "центральное" бегущее среднее (усреднять на период/2 влево и вправо от точки в прошлом), но тогда на краях (особенно на правом, который нам больше всего и интересен) остается незаполненное место размером период/2...
Я использую предлагаемый фильтр много лет при решении разных задач. Приплыл он ко мне под именем "фильтра восстановления сигнала Ушакова" от одного коллеги, недавнего (тогда) студента, очевидно, с какой-то лабы в вузе. С тех пор я несколько раз пытался найти первоисточник, но безуспешно. Да и бог с ним.
Фильтр является некоторой разновидностью медианного, смысл его работы в следующем. Берем три последовательных точки, считаем среднее по двум крайним. Если центральная точка уклоняется от этого среднего меньше, чем на предопределенную величину (уровень шума), то заменяем значение в центральной точке на среднее. В противном случае подтягиваем центральную точку к среднему на величину шума. Применяем этот алгоритм последовательно ко всем тройкам точек на интервале. Затем операцию повторяем заданное число раз. В результате шумы нивелируются, а гэпы более-менее остаются.
Код: Выделить всё
function Initialize()
{
IndicatorName = "UF";
PriceStudy = true;
AddInput("Input", Inputs.Price);
AddParameter("Period", 10);
AddParameter("Gap", 1.0); // % уровень разницы в значениях, трактуемый как несглаживаемый разрыв; меньшие разницы трактуются как шум
AddParameter("Iterations", 10); // число итераций фильтрации
AddSeries("UF", DrawAs.Line, Color.Black); // выходная серия индикатора
}
function Evaluate()
{
//-------------------------------------------------------------
// Фильтр восстановления сигнала (Ушакова)
// Для входной серии индикатора
Action<TA.Script.AdsInput, int, double, int, TA.Script.XSeries>
UFilter=(TA.Script.AdsInput _input, int _period, double _gap, int _iterations, TA.Script.XSeries _series_out) =>
// Для серии (в т.ч. другого индикатора)
// Action<TA.Script.XSeries, int, double, int, TA.Script.XSeries>
// UFilter=(TA.Script.XSeries _input, int _period, double _gap, int _iterations, TA.Script.XSeries _series_out) =>
{
if(_period<3) _period=3;
if(CurrentIndex<_period) return;
int from=_period-1;
double[] inpval=new double[_period];
double[] outval=new double[_period];
for(int i=-from, o=0; i<=0; i++, o++) inpval[o]=(outval[o]=_input[i]);
double g=_gap/100.0;
double avg, gapBound, v;
for(int iter=0; iter<_iterations; iter++)
{
for(int i=1; i<from; i++)
{
avg=(outval[i-1]+outval[i+1])/2.0;
gapBound=avg*g;
v=inpval[i];
outval[i]=Math.Max(v-gapBound, Math.Min(v+gapBound, avg));
}
avg=(outval[0]+outval[1])/2.0;
gapBound=avg*g;
v=inpval[0];
outval[0]=Math.Max(v-gapBound, Math.Min(v+gapBound, avg));
avg=(outval[from-1]+outval[from])/2.0;
gapBound=avg*g;
v=inpval[from];
outval[from]=Math.Max(v-gapBound, Math.Min(v+gapBound, avg));
}
for(int i=-from, o=0; i<=0; i++, o++) _series_out[i]=outval[o];
};
//-------------------------------------------------------------
if(CurrentIndex<MaxIndex) return; // отрисовка только на правом краю графика
UFilter(Input, (int)Period, (double)Gap, (int)Iterations, UF);
}
Как вы, очевидно, заметили, индикатор правит точки в прошлом, поэтому его нельзя использовать в тестировании/оптимизации стратегии. Для нормального тестирования следует перетащить ф-цию UFilter непосредственно в код стратегии и считать ее уже там на каждом шаге.
Число итераций можно делать достаточно большим (десятки и даже сотни).
На графике есть изломы; они как раз находятся на месте гэпов.
На каждом следующем шаге расчета форма кривой может существенно поменяться (при смене направления движения, особенно с гэпом).
Не стоит обольщаться "красивым" следованием за графиком цен, т.к. сглаживание идет по уже известным данным в прошлом.
Интерес представляют несколько (2-3...) крайних правых точек. Производную UF (в простейшем случае просто разность UF[0]-UF[-1]; обычно такая разность сильно шумит, но UF сам по себе сглажен, так что в данном случае шума нет) можно, например, использовать для построения экстраполятора в будущее ... (которое, как известно, от прошлого особо не зависит )
Можно пытаться использовать две кривые с разной степенью фильтрации вместо обычных бегущих средних. Отличие тут в том, что "быстрая" кривая, обеспечивая нужное сглаживание, может все же лучше следить за текущими колебаниями за счет того, что фильтр не имеет фиксированной частоты среза, т.е. адаптивен и не так сильно отстает от входных данных.