Всем доброго дня )))
Вопрос такой: почему индикаторы сделаны через дженерики таким образом, что могут не отрабатывать контракт?
На примере кода:
var candle = new TimeFrameCandle() ; IIndicator indicator = new ExponentialMovingAverage(); var result = indicator.Process(new CandleIndicatorValue(candle));
Если же взять Peak индикатор, то код внезапно начинает работать. Самое обидное, что контракт - всегда выполнен. Любой индикатор поддерживает интерфейс взаимодействия IIndicator, на вход Process подается IIndicatorValue. Но в одном случае IIndicatorValue обрабатывается корректно, а в другом - дает сбой.
Расследование показало, что это происходит из за вызова var newValue = input.GetValue<decimal>(); в EMA. Т.е. EMA исходит из того, что input строго decimal, хотя контрактом на EMA это не регламентировано.
Что делать?
Комментарии (3)
Вход или Создать аккаунт, Войдите или зарегистрируйтесь, чтобы оставить комментарий
Контракт это соглашение о том какие параметры должны обрабатываться методом или объектом. Все интерфейсы и базовые классы - это своего рода контракты для реализующих их классов. В данном случае линия наследования ExponentialMovingAverage:LengthIndicator, LengthIndicator:BaseIndicator, BaseIndicator:IIndicator показывает, что EMA должен отрабатывать все вызовы, которые присутствуют в IIndicator.
В IIndicator есть Process, который принимает IIndicatorValue. Т.е. любой потомок IIndicator должен обработать все, что есть IIndicatorValue. Я создаю new CandleIndicatorValue(new TimeFrameCandle...), который по наследования является IIndicatorValue.
Почему при передаче валидного IIndicatorValue в метод Process летит Эксепшен?
Теперь по пунктам:
Решения с точки зрения программирования: Если IIndicatorValue должен как то приводиться к decimal, то ИМХО стоит сделать метод public decimal ConvertToDecimal(), где все типы должны приводиться самим создателем наследника от IIndicatorValue. Если не все IIndicatorValue приводимы к decimal, то ИМХО стоит выделить приводимые к decimal в наследника IIndicatorValue. А методы типа IIndicatorValue Process(IIndicatorValue input); заменить на этого наследника для всех приводимых к decimal типов.
Вероятно, я не до конца понимаю объектную модель индикаторов 😀 Пока смотрел исходники появилась еще одна идея - типизировать сам IIndicator, а в наследниках указывать какие именно типа объектов может принимать Process. Возможно это тоже сможет упростить дело.
Тот же ChaikinVolatility делает input.GetValue<Candle>(); А ExponentialMovingAverage - input.GetValue<decimal>(); Оба в момент обработки входных данных.
Рассмотрим пример IndicatorsManager.cs из IndicatorsXaml.
Если есть общий список индикторов, то в момент их вызова (функция public void AddPoint(TimeFrameCandle candle)) Придется проверять типы этих индикторов, чтобы понять в каком виде им нужно предоставить Сandle.
Именно поэтому часть кода закомментирована - тот же SimpleMovingAverage просто не работает с данным кодом.
Я правильно понимаю, что единственный выход в данной ситуации это вереница if с проверкой какого типа индикатор и конвертацией Candle в подходящий для индикатора тип?