Всем доброго дня )))
Вопрос такой: почему индикаторы сделаны через дженерики таким образом, что могут не отрабатывать контракт?
На примере кода:
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 в подходящий для индикатора тип?