← Zurück

Exception при попытке расчитать ATR

Привет,

осваиваю Stocksharp, очень нравится :)

Нужно использовать индикатор ATR, нигде примеров не нашел, только на кодеплексе в тест кейсе.

Соответственно, пытаюсь его рассчитать:


            var atr = new AverageTrueRange { Length = 15 };
            ...
         
            TimeFrameCandle cdl = _candleManager.GetTimeFrameCandle(_candleToken, 1);

            try {
                atr.Process((CandleIndicatorValue)cdl);
            } catch (Exception ex) {
                Error(ex.Message + " stacktrace: " + ex.StackTrace);
            }

В результате на строке atr.Process происходит следующее исключение:


Invalid cast from 'System.Decimal' to 'StockSharp.Algo.Candles.Candle'.
 stacktrace:    at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
   at System.Decimal.System.IConvertible.ToType(Type type, IFormatProvider provider)
   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at Ecng.Common.Converter.To(Object value, Type destinationType)
   at Ecng.Common.Converter.To[T](Object value)
   at StockSharp.Algo.Indicators.CandleIndicatorValue.SetValue[T](T value)
   at StockSharp.Algo.Indicators.BaseIndicator`1.Process(IIndicatorValue input)
   at StockSharp.Algo.Indicators.Oscillator.AverageTrueRange.OnProcess(IIndicatorValue input)
   at StockSharp.Algo.Indicators.BaseIndicator`1.Process(IIndicatorValue input)
   at test.stocksharp.IndicatorTest.newCandle()

Версия стокшарпа: 4.0.3.

Подскажите куда копать, как побороть? В тесте вроде тоже производится просто каст свечи к CandleIndicatorValue

War dieses Thema hilfreich?

Thema teilen

Kommentare (18)

Anmelden oder Konto erstellen, Melden Sie sich an oder registrieren Sie sich, um einen Kommentar zu hinterlassen

esper
esper

Тест для ATR валится с такой же ошибкой:

Test method IndicatorsTest.Tests.AtrTest.Atr threw exception: System.InvalidCastException: Invalid cast from 'System.Decimal' to 'StockSharp.Algo.Candles.Candle'. Иными словами, руки до этого индикатора пока не дошли🙂 Есть желание помочь?

Mikhail Sukhov
Mikhail Sukhov

kenota: Соответственно, пытаюсь его рассчитать:

        var atr = new AverageTrueRange { Length = 15 };
        ...
     
        TimeFrameCandle cdl = _candleManager.GetTimeFrameCandle(_candleToken, 1);

        try {
            atr.Process((CandleIndicatorValue)cdl);
        } catch (Exception ex) {
            Error(ex.Message + " stacktrace: " + ex.StackTrace);
        }

Используйте IndicatorManager. Индикаторы как раз под него заточены, а не для того, чтобы самому свечки из CandleToken в Indicator перекладывать.
pehas
pehas

Mikhail Sukhov: Используйте IndicatorManager. Индикаторы как раз под него заточены, а не для того, чтобы самому свечки из CandleToken в Indicator перекладывать.

Не могли бы вы привести пример? Пробую так


            var candleToken = this._cm.RegisterTimeFrameCandles(security, this._timeFrame);
            var ATRToken = indicatorManager.RegisterIndicator(new AverageTrueRange{Length = 2}, new CandleTokenIndicatorSource(candleToken));

Значение "((AverageTrueRange)ATRToken.Indicator).isFormed" всегда false Как правильно инициализировать?

kenota
kenota Autor

Подтверждаю тоже самое что говорит pehas: обновил стокшарп до последней версии. Создаю indicatorManager однако индикатор никогда не строится. isFormed всегда false + IndicatorToken.Indicator.OnChanged ни разу не вызывается.

Попробую взять проект с кодекплекса и посмотреть дебагом что там в индикаторе творится :)

kenota
kenota Autor

Значит так, я нашел две проблемы и сделал хотфиксы, однако мне нужен ревью от кого нибудь кто больше понимает в проекте.

Проблема №1. Если использовать индикатор AverageTrueRange через IndicatorManager то он никогда не расчитывается. Проблема в файле AverageTrueRange.cs строчка 92:


/// <summary>
/// Возможно ли обработать входное значение.
/// </summary>
/// <param name="input">Входное значение.</param>
/// <returns>True, если возможно, иначе, false.</returns>
public override bool CanProcess(IIndicatorValue input)
{
            return TrueRange.CanProcess(input) && MovingAverage.CanProcess(input);
}

Тут проверяется может ли индикатор обработать входящее значение. ATR построили как комбинацию TrueRange (который принимает параметр Candle а результатом вычисления является double) и SMA (вход double и выход double). Вообщем это условие будет всегда ложно, т.к. в функцию передается свечка а MA ее не может обработать. Поэтому просто меняем ее на:


return TrueRange.CanProcess(input);

Проблема №2. После этого изменения у нас будет опять выбрасываться старый эксепшн, из-за которого я открыл этот топик. Ищем откуда растут ноги, смотрим на файл BaseIndicator.cs в районе строки 129 функция Process:


public virtual IIndicatorValue Process(IIndicatorValue input)	
{
	var value = OnProcess(input);

	this.LastValue = value;

	var result = value as IIndicatorValue ?? input.SetValue(value);

	if (IsFormed && !result.IsEmpty)
		RaiseChangedEvent(input, result);

	return result;
}

Экспешн происходит на строчке

var result = value as IIndicatorValue ?? input.SetValue(value);

Пытаемся понять что тут происходит. Ага, получается так: мы вычисляем значение индикатор через своего потомка (нас ведь создадут наследовав от BaseIndicator), но вот незадача: что бы быть type safe нам нужно вернуть значение IIndicatorValue, а что может вернуть наша текущая реализация мы не знаем. Поэтому мы смотрим: если то что вернул process является IIndicatorValue то, возвращаем его, иначе, мы считаем что значение должно быть того же типа что было входящее!

Вот тут то и приходит пушной зверек сверкая глазками. Дело в том что TrueRange принимает на вход Candle а возвращает Double. Здесь и происходит класс каст эксепшн.

К сожалению, я пока еще не знаю всю структуру проекта, поэтому не придумал ничего лучшего как сделать такой хотфикс, который поидее ничего не должен сломать, но починить этот эксепшн. Заменяем строку 135 на:


var result = value is decimal ? new DecimalIndicatorValue(value.To<decimal>()) :  value as IIndicatorValue ?? input.SetValue(value);

После этого atr начинает работать :)

Но я думаю это стоит починить по-правильному.

danl
danl

По поводу ATR, он работает без фиксов, если его инициализировать вот так:

IndicatorManager.RegisterIndicator(new AverageTrueRange(), new CandleTokenIndicatorSource(candleToken, BaseCandleIndicatorSource.ByClose));

То что там используется BaseCandleIndicatorSource.ByClose реального значения не имеет, так как исходный индикатор будет получать свечу целиком, но источник будет передавать в индикатор класс CandlePartIndicatorValue, который поддерживает тип decimal.

Наверное это не очень красиво, но работает. По поводу правильного решения, может следует сделать реализацию IIndicatorValue подобную той, которая в тесте, что бы она могла содержать любые значения и обучить текущие реализации содавать такие классы? Тогда выходные значения полностью развяжутся с входными, что, как мне кажется, логично. На эту тему нужно еще подумать.

kenota
Mikhail Sukhov
Mikhail Sukhov

danl: Наверное это не очень красиво, но работает. По поводу правильного решения, может следует сделать реализацию IIndicatorValue подобную той, которая в тесте, что бы она могла содержать любые значения и обучить текущие реализации содавать такие классы? Тогда выходные значения полностью развяжутся с входными, что, как мне кажется, логично. На эту тему нужно еще подумать.

Я так понял был еще и коммит. Не плохо бы отписывать в таких случаях. Я сделал небольшой фикс на коммит. Но не могу понять следующую ситуацию. Почему в SetValue null не является ошибочной ситуацией? Мне кажется это всегда ошибка. Нельзя передавать Candle == null.

danl
danl

Mikhail Sukhov: Я так понял был еще и коммит. Не плохо бы отписывать в таких случаях. Ок.

Mikhail Sukhov: Почему в SetValue null не является ошибочной ситуацией? Мне кажется это всегда ошибка. Нельзя передавать Candle == null. В данный момент получается, что SetValue, по сути, является методом, который создает, а не устанавливает значение. Так как были введены пустые значения (свойство IsEmpty), то нужен способ создавать и пустые значения, вот SetValue(null) и создает пустое значение. Можно сделать отдельный метод, который бедт создавать пустое значение, но тогда придется править индикаторы, что бы они null туда никогда не передавали, в принципе большой сложности не предстваляет. Кроме того, нужно что бы в тестовом проекте, то же происходило падение, при вызове SetValue(null), а то в тесте работает, а жизни нет (я в основном из-за этого сделал, что бы индикаторы не падали).

Dmitriy Klimov
Dmitriy Klimov

danl: По поводу ATR, он работает без фиксов, если его инициализировать вот так:

IndicatorManager.RegisterIndicator(new AverageTrueRange(), new CandleTokenIndicatorSource(candleToken, BaseCandleIndicatorSource.ByClose));

> 
> То что там используется BaseCandleIndicatorSource.ByClose реального значения не имеет, так как исходный индикатор будет получать свечу целиком, но источник будет передавать в индикатор класс CandlePartIndicatorValue, который поддерживает тип decimal.
> 
> Наверное это не очень красиво, но работает. По поводу правильного решения, может следует сделать реализацию IIndicatorValue подобную той, которая в тесте, что бы она могла содержать любые значения и обучить текущие реализации содавать такие классы? Тогда выходные значения полностью развяжутся с входными, что, как мне кажется, логично. На эту тему нужно еще подумать.

Добрый день.

Создаю индикатор на базе ATR. Столкнулся с описанной выше проблемой: генерируется исключение при отправке свечки в индикатор. Я так понимаю, рекомендуется использовать IndicatorManager для наполнения индикаторов данными. Но тогда, получается, мне придется при создании индикатора отправлять ему данные об IndicatorManager и CandleTokenIndicatorSource. Получается сильная зависимость индикаторов от других классов, хотя, вроде бы, особой необходимости в этом нет.

Как коллеги решают данную проблему? Или проблемы нет, я просто не там ищу?
kenota
kenota Autor

Спасибо, буду знать что можно так инициализировать.

russ
russ

Аналогичная проблема, борюсь с фракталами

[left]try
                    {
                        _strategy.FractalsValues.Process((CandleIndicatorValue)candle);
                    }
                    catch (System.Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }[/left]

Выдает ошибку

Недопустимое приведение "System.Int32" к "StockSharp.Algo.Candles.Candle".

Что не так делаю?

Посмотрел дебагом индиктор фракталов, падает в

public override ComplexIndicatorValue OnProcess(IIndicatorValue input)

в

return base.OnProcess(new ShiftedIndicatorValue(0, input.SetValue(0)));
Alexander
Alexander

russ: Аналогичная проблема, борюсь с фракталами

[left]try { _strategy.FractalsValues.Process((CandleIndicatorValue)candle); } catch (System.Exception ex) { MessageBox.Show(ex.Message); }[/left]

> 
> 
> Выдает ошибку
> 
> Недопустимое приведение "System.Int32" к "StockSharp.Algo.Candles.Candle".
> 
> Что не так делаю?
> 
> Посмотрел дебагом индиктор фракталов, падает в
> ```csharp
public override ComplexIndicatorValue OnProcess(IIndicatorValue input)

в

return base.OnProcess(new ShiftedIndicatorValue(0, input.SetValue(0)));



Ну видимо candle у вас Int32, а вы пытаетесь не к тому типу привести.
russ
russ

Alexander Mukhanchikov:

russ: Аналогичная проблема, борюсь с фракталами

[left]try { _strategy.FractalsValues.Process((CandleIndicatorValue)candle); } catch (System.Exception ex) { MessageBox.Show(ex.Message); }[/left]

> >
> >
> > Выдает ошибку
> >
> > Недопустимое приведение "System.Int32" к "StockSharp.Algo.Candles.Candle".
> >
> > Что не так делаю?
> >
> > Посмотрел дебагом индиктор фракталов, падает в
> > ```csharp
public override ComplexIndicatorValue OnProcess(IIndicatorValue input)

в

return base.OnProcess(new ShiftedIndicatorValue(0, input.SetValue(0)));

> 
> 
> Ну видимо candle у вас Int32, а вы пытаетесь не к тому типу привести.

Да, ошибку я понял, но я пользуюсь индикаторами, которые идут в S#, получается они могут не работать?
Alexander
Alexander

russ:

Alexander Mukhanchikov:

russ: Аналогичная проблема, борюсь с фракталами

[left]try { _strategy.FractalsValues.Process((CandleIndicatorValue)candle); } catch (System.Exception ex) { MessageBox.Show(ex.Message); }[/left]

> > >
> > >
> > > Выдает ошибку
> > >
> > > Недопустимое приведение "System.Int32" к "StockSharp.Algo.Candles.Candle".
> > >
> > > Что не так делаю?
> > >
> > > Посмотрел дебагом индиктор фракталов, падает в
> > > ```csharp
public override ComplexIndicatorValue OnProcess(IIndicatorValue input)

в

return base.OnProcess(new ShiftedIndicatorValue(0, input.SetValue(0)));

> >
> >
> > Ну видимо candle у вас Int32, а вы пытаетесь не к тому типу привести.
> 
> Да, ошибку я понял, но я пользуюсь индикаторами, которые идут в S#, получается они могут не работать?


> _strategy.FractalsValues.Process((CandleIndicatorValue)candle);

Это ваш код, следовательно, ошибка у вас, а не в S#.
Вы приводите Int32 к CandleIndicatorValue, а не S#, разве нет?
russ
russ

_strategy.FractalsValues.Process((CandleIndicatorValue)candle);

Это ваш код, следовательно, ошибка у вас, а не в S#. Вы приводите Int32 к CandleIndicatorValue, а не S#, разве нет?

Да, это мой код, но candle у меня это объект типа TimeFrameCandle. Если посмотреть OnProcess индикатора фракталов, который идет в библиотеке индикаторов, то:

public override ComplexIndicatorValue OnProcess(IIndicatorValue input)
{
    var candle = input.GetValue<Candle>();
    ....
}

Насколько я понял, на вход он получает свечу ```csharp input.GetValue<Candle>();

поэтому свечу я ему и подал ```csharp
_strategy.FractalsValues.Process((CandleIndicatorValue)candle);

а ошибка происходит в том же OnProcess в строке

return base.OnProcess(new ShiftedIndicatorValue(0, input.SetValue(0)));

и ошибка понятна, потому-что input у нас типа Candle а мы задаем ему Int32. Поэтому тут два варианта, либо на вход нужно подавать не свечу, а что-то другое, либо в input.SetValue(0) задавать не 0, а какую-то свечу. Я только начал разбираться, поэтому еще не до конца понял логику этих индикаторов, подскажите плз.

Sergey Masyura
Sergey Masyura

_strategy.FractalsValues.Process((CandleIndicatorValue)candle);

Это ваш код, следовательно, ошибка у вас, а не в S#. Вы приводите Int32 к CandleIndicatorValue, а не S#, разве нет?

Да, это мой код, но candle у меня это объект типа TimeFrameCandle. Если посмотреть OnProcess индикатора фракталов, который идет в библиотеке индикаторов, то:

public override ComplexIndicatorValue OnProcess(IIndicatorValue input)
{
    var candle = input.GetValue<Candle>();
    ....
}

Насколько я понял, на вход он получает свечу ```csharp input.GetValue<Candle>();

поэтому свечу я ему и подал ```csharp
_strategy.FractalsValues.Process((CandleIndicatorValue)candle);

а ошибка происходит в том же OnProcess в строке

return base.OnProcess(new ShiftedIndicatorValue(0, input.SetValue(0)));

и ошибка понятна, потому-что input у нас типа Candle а мы задаем ему Int32. Поэтому тут два варианта, либо на вход нужно подавать не свечу, а что-то другое, либо в input.SetValue(0) задавать не 0, а какую-то свечу. Я только начал разбираться, поэтому еще не до конца понял логику этих индикаторов, подскажите плз.

Если на входе свеча, а на выходе число, то там используется CandlePartIndicatorValue, а не CandleIndicatorValue. Весь исходный код индикаторов открыт, так что если есть возможность посмотреть самому - вполне логично этой возможностью воспользоваться.

russ
russ

sergey.masyura: Если на входе свеча, а на выходе число, то там используется CandlePartIndicatorValue, а не CandleIndicatorValue. Весь исходный код индикаторов открыт, так что если есть возможность посмотреть самому - вполне логично этой возможностью воспользоваться. Я честно уже долго сижу разбираюсь, не получается 🤨 И как CandlePartIndicatorValue на вход подать вместо CandleIndicatorValue ума не приложу.

Sergey Masyura
Sergey Masyura

russ:

sergey.masyura: Если на входе свеча, а на выходе число, то там используется CandlePartIndicatorValue, а не CandleIndicatorValue. Весь исходный код индикаторов открыт, так что если есть возможность посмотреть самому - вполне логично этой возможностью воспользоваться. Я честно уже долго сижу разбираюсь, не получается 🤨 И как CandlePartIndicatorValue на вход подать вместо CandleIndicatorValue ума не приложу.

Никак. Но если возникла такая необходимость, то можно отнаследоваться от BaseCandleIndicatorSource и вызывать у него NewCandles(candles)


	internal class MyIndicatorSource : BaseCandleIndicatorSource
	{
		public MyIndicatorSource () : base(BaseCandleIndicatorSource.ByClose)
		{
			
		}
	}