← Zurück

Добавление своего индикатора на график

Здравствуйте. Вопрос по добавлению своего индикатора на график. Индикатор создан на базе индикаторов из stocksharp.codeplex только у итогового изменено название с highest на step1, как библиотека классов. При добавлении его на график появляется ошибка - Неявное преобразование типа "Swing.Step1" в "StockSharp.Algo.Indicators.IIndicator" невозможно. Существует явное преобразование (возможно, отсутствует приведение). В каком направлении двигаться?

War dieses Thema hilfreich?

Thema teilen

Kommentare (12)

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

IvanB
IvanB

Здравствуйте Compressor. Нам нужен код, как переопределяете индикатор Highest и как затем используете новый, чтобы помочь Вам.

Самунджян Артем
Compressor
Compressor Autor

Код, в котором добавляется индикатор

namespace Lesson7
{
    /// <summary>
    /// Логика взаимодействия для MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //создаем лог менеджер
            _logManager= new LogManager();
            //добавляем источник информации трейдера
            _logManager.Sources.Add(Connection.Trader);
            //добавляем отображение с помощью монитора
            _logManager.Listeners.Add(new GuiLogListener(_monitor));
            _candleManager = new CandleManager(Connection.Trader);
            _monitor.Show();
            Connection.Trader.NewMyTrades += DrawTradesOnChart;
        }
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            if (Connection.Trader != null && Connection.Trader.IsConnected)
            {
                Connection.Trader.Dispose();
            }
            Thread.CurrentThread.Abort();
            base.OnClosing(e);
        }
        /// <summary>
        /// Окно для отображения графика со свечками
        /// </summary>
        private ChartWindow _chartWindow;

        private readonly LogManager _logManager;
        private readonly MonitorWindow _monitor= new MonitorWindow();

        /*----------Элементы для графика------------*/
        private ChartCandleElement _candlesElem;
        private ChartTradeElement _tradeElement;
        private ChartArea _area;
        private ChartIndicatorElement _indichElem;
        private ChartIndicatorElement _indiclElem;
        private ChartIndicatorElement _step1Elem;
        private ChartIndicatorElement _bollingerElem;
        /*------------------------------------------*/

        /*--Получение свечек из торговой платформы-*/
        private CandleSeries _series;
        private readonly CandleManager _candleManager;
        /*-----------------------------------------*/

        private void ChartClick(object sender, RoutedEventArgs e)
        {
           //создаем окно с графиком со всеми элементами
            InitChart();
            //передаем исторически свечки на график 
            GethistoryCandles().ForEach(Draw);
            //запускаем стратегию
            InitStrategy();
            //подписываемся на свечки из торговой платформы
            _series.ProcessCandle += Draw;
            //запускаем получение свечек из торговой платформы
            _candleManager.Start(_series);
        
        }
        /// <summary>
        /// Исторические свечки
        /// </summary>
        /// <returns></returns>
        private IEnumerable<Candle> GethistoryCandles()
        {
            //закачка из текстового файла
            /* return  CultureInfo.InvariantCulture.DoInCulture(() => File.ReadAllLines("RIZ2.txt").Select(line =>
            {
                var timeFrame = (TimeSpan)_series.Arg;
                var parts = line.Split(',');
                var time = (parts[0] + parts[1]).ToDateTime("yyyyMMddHHmmss");
                return (Candle)new TimeFrameCandle
                {
                    OpenPrice = parts[2].To<decimal>(),
                    HighPrice = parts[3].To<decimal>(),
                    LowPrice = parts[4].To<decimal>(),
                    ClosePrice = parts[5].To<decimal>(),
                    TimeFrame = timeFrame,
                    OpenTime = time,
                    CloseTime = time + timeFrame,
                    TotalVolume = parts[6].To<decimal>(),
                    Security = Connection.SelectedSecurity,
                    State = CandleStates.Finished,
                };
            }).ToArray());*/
            //var candles = Finam.History.GetCandles(Connection.SelectedSecurity, DateTime.Now - TimeSpan.FromDays(1), DateTime.Now - TimeSpan.FromDays(1), (TimeSpan) _series.Arg);
            return   Finam.History.GetHistoryCandles(Connection.SelectedSecurity, TimeSpan.FromMinutes(1), DateTime.Now - TimeSpan.FromDays(10), DateTime.Now - TimeSpan.FromDays(1)).ToArray();
        }

        /// <summary>
        /// Инициализируем чарт
        /// </summary>
        private void InitChart()
        {
            var security = Connection.SelectedSecurity;
            _series = new CandleSeries(typeof(TimeFrameCandle), security, TimeSpan.FromSeconds(20) /*TimeFrame.Value.Value.TimeOfDay*/);
            _series.WorkingTime.Times[0].Min = TimeSpan.FromHours(4);
            _series.WorkingTime.Times[0].Max = TimeSpan.FromHours(23);
            //создаем окно с графиком свечек
            _chartWindow = new ChartWindow
            {
                Title = "{0} {1}".Put(security.Code, _series.Arg)
            };
            
            _chartWindow.MakeHideable();

            /* ----- Добавляем все нужные участки для нашего графика*/
            _area = new ChartArea();
            _chartWindow.Chart.Areas.Add(_area);

            _candlesElem = new ChartCandleElement();
            _area.Elements.Add(_candlesElem);

            _tradeElement= new ChartTradeElement();
            _area.Elements.Add(_tradeElement);

            _indichElem = new ChartIndicatorElement() { Title = "Moving", Indicator = new Highest() { Name = "Moving", Length = 2} };
            _area.Elements.Add(_indichElem);

            _indiclElem = new ChartIndicatorElement() { Title = "Moving", Indicator = new Lowest() { Name = "Moving", Length = 2 } };
            _area.Elements.Add(_indiclElem);

            _step1Elem = new ChartIndicatorElement() { Title = "Moving", Indicator = new Step1() { Name = "Moving", Length = 2 } };
            _area.Elements.Add(_step1Elem);

            _bollingerElem = new ChartIndicatorElement() { Title = "Bollinger", Indicator = new BollingerBands() { Name = "Bollinger", Length = 20, Width = 2 } };
            _area.Elements.Add(_bollingerElem);
            /*----------------------------------------------------*/
            _chartWindow.Show();
        }

        /// <summary>
        /// отрисовываем сделки стратегии
        /// </summary>
        /// <param name="mytrades"></param>
        private void DrawTradesOnChart(IEnumerable<MyTrade> mytrades)
        {
            if (_tradeElement == null) return;
            this.GuiAsync(() => mytrades.ForEach(t => _chartWindow.Chart.ProcessValues(t.Trade.Time,new Dictionary<IChartElement, object>()
                                                                                                        {
                                                                                                            {_tradeElement,t}
                                                                                                        })));
        }
        /// <summary>
        /// создаем и запускаем стратегию
        /// </summary>
        private void InitStrategy()
        {
            var bs = new BollingerStrategy((BollingerBands) _bollingerElem.Indicator, _series)
                         {
                             Trader = Connection.Trader,
                             Security = Connection.SelectedSecurity,
                             Portfolio = Connection.SelectedPortfolio,
                             Volume = 1,
                         };
            bs.NewMyTrades += DrawTradesOnChart;
            bs.Start();

        }
        /// <summary>
        /// Основной алгоритм прорисовки он-лайн и законченных свечек
        /// </summary>
        /// <param name="candle"></param>
        private void Draw(Candle candle)
        {
            if (candle.State == CandleStates.Finished || candle.State==CandleStates.Changed)
            {
                var final = candle.State == CandleStates.Finished;
                //значения у индикаторы в данный момент времени(finished,changed)
                var val = _bollingerElem.Indicator.Process(new DecimalIndicatorValue(candle.ClosePrice) {IsFinal =final});
                var bollingerValue = new ChartIndicatorValue(_bollingerElem.Indicator, val);

                var valh = _indichElem.Indicator.Process(new DecimalIndicatorValue(candle.HighPrice) { IsFinal = final });
                var indichValue = new ChartIndicatorValue(_indichElem.Indicator, valh);

                var vall = _indiclElem.Indicator.Process(new DecimalIndicatorValue(candle.LowPrice) { IsFinal = final });
                var indiclValue = new ChartIndicatorValue(_indiclElem.Indicator, vall);

                var st1 = _step1Elem.Indicator.Process(new DecimalIndicatorValue(candle.HighPrice) { IsFinal = final });
                var stValue = new ChartIndicatorValue(_indichElem.Indicator, st1);

                //определеяем время последней свечки
                var timeFrame = (TimeSpan) _series.Arg;
                var time = timeFrame.GetCandleBounds(_series.Security).Min-timeFrame;//находим последнее время свечки
                //добавляем свечки на чарт
                if(candle.State==CandleStates.Finished || (candle.State==CandleStates.Changed && candle.OpenTime>=time))
                { 
                    this.GuiAsync(() => _chartWindow.Chart.ProcessValues(candle.OpenTime, new Dictionary<IChartElement, object>
                                                                                          {
                                                                                              {_candlesElem, candle},
                                                                                  //            {_bollingerElem, bollingerValue},
                                                                                              {_indichElem, indichValue},
                                                                                              {_indiclElem, indiclValue},
                                                                                              {_step1Elem, stValue},
                                                                                          }));
                }
            }
        }
    }
}

Код добавляемого индикатора Step1

namespace Swing
{
    public class Step1 : LengthIndicator<decimal>
	{
       //         private readonly CandleSeries _series;

   //     private bool NoActiveOrders { get { return Orders.Count(o => o.State == OrderStates.Active) == 0; } }
   //     public Step1(CandleSeries series)
   //     {
            
   //         _series = series;
    //    }
		/// <summary>
		/// Создать <see cref="Highest"/>.
		/// </summary>
		public Step1()
			: base(typeof(decimal))
		{
		}

		/// <summary>
		/// Обработать входное значение.
		/// </summary>
		/// <param name="input">Входное значение.</param>
		/// <returns>Результирующее значение.</returns>
		public override decimal OnProcess(IIndicatorValue input)
		{
			var newValue = input.GetValue<decimal>();

			var lastValue = LastValue;

			if (Buffer.Count == 0)
			{
				// Инициализируем при добавлении первого значения
				lastValue = newValue;
			}

			// добавляем новое начало
			Buffer.Add(newValue);

			if (newValue > lastValue)
			{
				// Новое значение и есть экстремум 
				lastValue = newValue;
			}

			if (Buffer.Count > Length)
			{
				// удаляется экстремум, для поиска нового значения необходим проход по всему буфферу
				if (Buffer[0] == lastValue && lastValue != newValue)
				{
					Buffer.RemoveAt(0);

					// ищем новый экстремум
					lastValue = Buffer.Aggregate(newValue, (current, t) => Math.Max(t, current));
				}
				else
				{
					// удаляем хвостовое значение
					Buffer.RemoveAt(0);
				}
			}

			return lastValue;
		}
	}
}

Его отличие от Highest в том, что он добавляется из своей библиотеки, а не из библиотеки StoskSharp'а.

IvanB
IvanB

Просмотрев код, видно, что есть ошибка в следующих строках:

                var st1 = _step1Elem.Indicator.Process(new DecimalIndicatorValue(candle.HighPrice) { IsFinal = final });
                var stValue = new ChartIndicatorValue(_indichElem.Indicator, st1);

Во второй строке Вы хотели бы получить значение индикатора _step1Elem.Indicator, но используете индикатор _indichElem.Indicator. Так правильно:

                var st1 = _step1Elem.Indicator.Process(new DecimalIndicatorValue(candle.HighPrice) { IsFinal = final });
                var stValue = new ChartIndicatorValue(_step1Elem.Indicator, st1);

Даже если выполнить проект с Вашим кодом и без исправления указанной выше ошибки, то проект выполняется успешно. Есть предположение, что Вы используете разные версии модулей S# в разных сборках своего приложения, это может привести к исключению о котором Вы писали. Проверьте версии подключенных модулей, если ошибку не удастся устранить, пришлите архив с проектами.

Compressor
Compressor Autor

Не помогло, в 132 строке все равно выдает ошибку.

IvanB
IvanB

Compressor: Не помогло, в 132 строке все равно выдает ошибку.

Подключенные сборки проверяли? Пришлите архив проекта на электронную почту iv_qul@list.ru

Compressor
Compressor Autor

После изменений, связанных с переопределением библиотек, индикатор не реагирует на параметр Length, везде берет значение 1.

IvanB
IvanB

Compressor: После изменений, связанных с переопределением библиотек, индикатор не реагирует на параметр Length, везде берет значение 1.

Привожу в качестве примера код индикатора, который адекватно работает, в том числе принимает значение Length:

public class Step2 : LengthIndicator<decimal>
 {
     /// <summary>
		/// Создать
		/// </summary>
		public Step2()
			: base(typeof(decimal))
		{
		}

     public override bool IsFormed
     {
         get
         {
             return true;
         }
     }

		/// <summary>
		/// Обработать входное значение.
		/// </summary>
		/// <param name="input">Входное значение.</param>
		/// <returns>Результирующее значение.</returns>
		protected override decimal OnProcess(IIndicatorValue input)
		{
			var val = input.GetValue<decimal>();

		    var newValue = val + Length;
         return newValue;
		}
 }

Замечу, что здесь наследуемый класс LengthIndicator<> взят из S#.

Compressor
Compressor Autor

К сожалению и этот вариант не работает правильно, если посмотреть график, то коричневая линия это как раз Максимум за 2 бара плюс shift (она производная от розовой без смещения на верх), если присмотреться то вершины образуют макс только на одном баре и не продляются на второй, а вот желтая линия графика это правильная линия, она держит максимум 2 бара.

Compressor
Compressor Autor

В последнем варианте величина Length просто поднимает или опускает график сильнее, но не влияет на базу расчета индикатора.

IvanB
IvanB

Compressor: В последнем варианте величина Length просто поднимает или опускает график сильнее, но не влияет на базу расчета индикатора.

Правильно, свойство Length только хранит целочисленное значение и никак не влияет на работу индикатора, если Вы его не используете. Вам необходимо самостоятельно использовать это свойство (Length) для реализации алгоритма индикатора. Вы можете посмотреть код индикатора Highest, там в переопределенном методе OnProcess обеспечивается управление историческим буфером (Buffer) с использованием свойства Length.

Compressor
Compressor Autor

По примеру Highest параметр LastValue не существует в текущем контексте.

IvanB
IvanB

Compressor: По примеру Highest параметр LastValue не существует в текущем контексте.

Я полагаю, Вы используете старый вариант, попробуйте это:

public class Highest : LengthIndicator<decimal>
	{
		/// <summary>
		/// Создать <see cref="Highest"/>.
		/// </summary>
		public Highest()
			: base(typeof(decimal))
		{
		}

		/// <summary>
		/// Обработать входное значение.
		/// </summary>
		/// <param name="input">Входное значение.</param>
		/// <returns>Результирующее значение.</returns>
		protected override decimal OnProcess(IIndicatorValue input)
		{
			var newValue = input.GetValue<decimal>();

			var lastValue = Buffer.Count == 0 ? newValue : this.GetCurrentValue();

			// добавляем новое начало
			if (input.IsFinal)
				Buffer.Add(newValue);

			if (newValue > lastValue)
			{
				// Новое значение и есть экстремум 
				lastValue = newValue;
			}

			if (Buffer.Count > Length)
			{
				var first = Buffer[0];

				// удаляем хвостовое значение
				if (input.IsFinal)
					Buffer.RemoveAt(0);

				// удаляется экстремум, для поиска нового значения необходим проход по всему буфферу
				if (first == lastValue && lastValue != newValue)
				{
					// ищем новый экстремум
					lastValue = Buffer.Aggregate(newValue, (current, t) => Math.Max(t, current));
				}
			}

			return lastValue;
		}
	}

Рекомендую пройти уроки C#, многое будет понятно, меньше будет подобных проблем.