← 返回

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

本主题对您有帮助吗?

分享主题

评论 (18)

登录创建账户, 登录或注册以发表评论

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 作者

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

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

kenota
kenota 作者

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

Проблема №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 作者

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

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)
		{
			
		}
	}