Перфоманс тестинга на истории


Перфоманс тестинга на истории
Atom Reply
8/21/2011


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

Я проверял производительность на примере SampleHistoryTesting. Тесты показали (мерил через dotTrace), что основной тормоз - это генерация свечек (48% уходит на CandleManager). Ускорить алго практически невозможно, так как он ускорялся и не раз. Но есть решение - использовать уже готовые свечки. В Гидре начиная с 3.2 версии появилась возможность сжатия свечек. Схема работы для бэктестера не очень удобная, так как за раз не получится загрузить и сжать все свечки (банально не хватит памяти). Поэтому я предлагаю всем заинтересованным пользователям откликнуться на это предложение и помочь в решении проблемы.

Исходники Гидры доступны каждому, они есть в дистрибутиве. Мое предложение такое. Необходимо сделать в Гидре (авто?) компрессор сделок по большому диапазону в отдельном окне. Тоесть, для такого компрессора не нужно будет выбирать и загружать сделки, а достаточно указать дни, с какого по какой нужно сжать сделки в свечки, и дальше он начнет их сжатие, загружая день за днем.

Если откликнутся желающие (а я надеюсь что такие найдутся), то со стороны S# мы сделаем авто загрузку свечек из истории через класс CandleManager. Как итог, ускорим бэктестинг стратегий, которые требуют свечки, причем существенное ускорение.

Других методов пока не придумал, если они вообще существуют. К сожалению, обратная сторона медали точного тестирования - это его производительность.



Thanks:




65 Answers
< 1 2 3 
Mikhail Sukhov

Avatar
Articles author Programmer Trader
Date: 10/15/2011
Reply


bender Перейти

Мы, кажется, друг друга не понимаем.)))
У меня последняя версия, код, который я привёл, в ней действительно не существует. Но я про то, что этот код в последней версии не работает, хотя судя по докам https://stocksharp.ru/doc/ должен работать, отсюда и возник этот вопрос. А где нибудь можно почитать про новый CandleManager?


Доки пока нет. Изменения вкратце. CandleManager теперь не строит свечки. Он лишь получает их от ICandleSource и передает дальше + сохраняет в контейер. Свечки строит CandleBuilder. Он принимает сделки от свои ITradeSource и через ICandleFactory превращается их в Candle.
Topic starter
Thanks:

bender

Avatar
Date: 10/17/2011
Reply


Добавил окно с возможностью формирования свечек по интервалу дат, пока только таймфрейм, тики и объём, остальные пока не успел. Кнопка авто, автоматически выставляет интервал по всем загруженным сделкам, если свечки в указанный каталог уже сохранялись - то от последней свечи до последней сделки. Т.к. писал это для более ранней версии, сейчас там есть уже не нужные вещи вроде xml файла, постараюсь на неделе доработать. Залил на кодеплекс
Thanks:

Mikhail Sukhov

Avatar
Articles author Programmer Trader
Date: 10/18/2011
Reply


bender Перейти
Добавил окно с возможностью формирования свечек по интервалу дат, пока только таймфрейм, тики и объём, остальные пока не успел. Кнопка авто, автоматически выставляет интервал по всем загруженным сделкам, если свечки в указанный каталог уже сохранялись - то от последней свечи до последней сделки. Т.к. писал это для более ранней версии, сейчас там есть уже не нужные вещи вроде xml файла, постараюсь на неделе доработать. Залил на кодеплекс


Спасибо. Глянул мельком, замечаний море. Будем улучшать знания в C#. Скажите, когда будет финальный коммит, и можно будет пройтись по пунктам.

Сделайте, пожалуйста, табуляцию табами, а не пробелами. Сейчас иначе весь файл переформатируется студией, если его другие начнут править. Советую поставить R#, он покажет так же часть ошибок в коде.
Topic starter
Thanks:

bender

Avatar
Date: 10/19/2011
Reply


Mikhail Sukhov Перейти

Спасибо. Глянул мельком, замечаний море. Будем улучшать знания в C#. Скажите, когда будет финальный коммит, и можно будет пройтись по пунктам.

Сделайте, пожалуйста, табуляцию табами, а не пробелами. Сейчас иначе весь файл переформатируется студией, если его другие начнут править. Советую поставить R#, он покажет так же часть ошибок в коде.


Поставил решарпер, в связи с этим несколько вопросов. Он в основном выдает три замечания - убрать this, использовать var и переименовать обработчики событий. Я думал, что var нужен только для анонимных типов, но если надо поменяю. А обработчики событий из, например, StartStop_Click предлагает переименовать в StartStopClick, хотя везде они поименованы через подчёркивание. Может он у меня настроен не так?
Thanks:

Mikhail Sukhov

Avatar
Articles author Programmer Trader
Date: 10/19/2011
Reply


bender Перейти
Поставил решарпер, в связи с этим несколько вопросов. Он в основном выдает три замечания - убрать this, использовать var и переименовать обработчики событий. Я думал, что var нужен только для анонимных типов, но если надо поменяю.


Предупреждений по var не увидел. Возможно у нас разные настройки. Обычно я var использую, когда тип переменной слишком большой. Анонимный тип тут не при чем.

bender Перейти

А обработчики событий из, например, StartStop_Click предлагает переименовать в StartStopClick, хотя везде они поименованы через подчёркивание. Может он у меня настроен не так?


Да скорее всего.

Мне больше сама структура кода смущает. Например, CandlesCompress уж слишком сложен в поминании. Там явно переизбыток лямбд.
Topic starter
Thanks:

Garic

Avatar
Date: 12/18/2011
Reply


Сломалась генерация свечек (4.0.9)

Я так понимаю в CandlesCompress вместо
Код
candleManager.Sources.Add(new CandleBuilder(new RawConvertableCandleBuilderSource<Trade>(trades)));

надо
Код
candleManager.Sources.Add(new CandleBuilder(new RawConvertableCandleBuilderSource<Trade>(trades)) { IsSyncRegister = true }); 
Thanks:

Alexander

Avatar
Date: 12/18/2011
Reply


Garic Перейти
Сломалась генерация свечек (4.0.9)

Я так понимаю в CandlesCompress вместо
Код
candleManager.Sources.Add(new CandleBuilder(new RawConvertableCandleBuilderSource<Trade>(trades)));

надо
Код
candleManager.Sources.Add(new CandleBuilder(new RawConvertableCandleBuilderSource<Trade>(trades)) { IsSyncRegister = true }); 


Да, верно.
Залил.
Thanks:

Garic

Avatar
Date: 12/26/2011
Reply


А как тестеру скормить предварительно расчитанные свечи?
Ничего подходящего не нашёл, а без них он работает существенно медленней чем в старых версиях (сравниваю с 3.2.7)
Thanks:

Mikhail Sukhov

Avatar
Articles author Programmer Trader
Date: 12/26/2011
Reply


Garic Перейти
А как тестеру скормить предварительно расчитанные свечи?
Ничего подходящего не нашёл, а без них он работает существенно медленней чем в старых версиях (сравниваю с 3.2.7)


ICandleSource
Topic starter
Thanks:

Garic

Avatar
Date: 12/27/2011
Reply


Mikhail Sukhov Перейти
ICandleSource


Т.е. нужно сделать по типу своего CandleBuilder?
Он должен генерить события CandlesStarted, CandlesChanged, CandlesFinished

Что должно их провоцировать (какое внешнее событие), за какой период должны отдаваться свечки, по сколько штук за один раз?
Не видя внутренности сложно понять.
Я так понимаю, отдаваться должны свечи всех зарегистрированных типов?
Thanks:

Mikhail Sukhov

Avatar
Articles author Programmer Trader
Date: 12/27/2011
Reply


Garic Перейти
Mikhail Sukhov Перейти
ICandleSource


Т.е. нужно сделать по типу своего CandleBuilder?
Он должен генерить события CandlesStarted, CandlesChanged, CandlesFinished


Я бы сказал по аналогии с SmartCandleSource

Код
/// <summary>
	/// SmartCOM источник свечек типа <see cref="TimeFrameCandle"/>.
	/// </summary>
	public class SmartCandleSource : ICandleSource
	{
		/// <summary>
		/// Создать <see cref="SmartCandleSource"/>.
		/// </summary>
		/// <param name="trader">Шлюз к SmartCOM, через событие <see cref="SmartTrader.NewHistoryCandles"/> будут получаться свечки.</param>
		public SmartCandleSource(SmartTrader trader)
		{
			if (trader == null)
				throw new ArgumentNullException("trader");

			Trader = trader;
			Trader.NewHistoryCandles += OnNewHistoryCandles;
			From = Trader.MarketTime.Date - TimeSpan.FromDays(5);
		}

		/// <summary>
		/// Начальная дата, с которой необходимо получать данные из SmartCOM. По-умолчанию равно -5 дней от начала текущей сессии.
		/// </summary>
		public DateTime From { get; set; }

		/// <summary>
		/// Шлюз к SmartCOM, через событие <see cref="SmartTrader.NewHistoryCandles"/> будут получаться свечки.
		/// </summary>
		public SmartTrader Trader { get; private set; }

		IEnumerable<CandleToken> ICandleSource.Tokens
		{
			get { return Trader.HistoryCandleTokens; }
		}

		private Action<CandleToken, IEnumerable<Candle>> _candlesStarted;

		event Action<CandleToken, IEnumerable<Candle>> ICandleSource.CandlesStarted
		{
			add { _candlesStarted += value; }
			remove { _candlesStarted -= value; }
		}

		event Action<CandleToken, IEnumerable<Candle>> ICandleSource.CandlesChanged
		{
			add { }
			remove { }
		}

		private Action<CandleToken, IEnumerable<Candle>> _candlesFinished;

		event Action<CandleToken, IEnumerable<Candle>> ICandleSource.CandlesFinished
		{
			add { _candlesFinished += value; }
			remove { _candlesFinished -= value; }
		}

		event Action<Exception> ICandleSource.ProcessDataError
		{
			add { }
			remove { }
		}

		CandleToken ICandleSource.GetToken(Type candleType, Security security, object arg)
		{
			CheckTypes(candleType, arg);
			return Trader.HistoryCandleTokens.FirstOrDefault(c => c.Security == security && c.Arg == arg);
		}

		bool ICandleSource.IsSupport(Type candleType, Security security, object arg)
		{
			return candleType == typeof(TimeFrameCandle) && security.Trader == Trader && arg is TimeSpan && SmartTimeFrames.CanConvert((TimeSpan)arg);
		}

		CandleToken ICandleSource.Register(Type candleType, Security security, object arg)
		{
			if (((ICandleSource)this).GetToken(candleType, security, arg) != null)
				throw new ArgumentException("Группировка свечек уже зарегистрирована ранее с переданными параметрами.");

			CheckTypes(candleType, arg);

			var token = Trader.RegisterHistoryRealTimeCandles(security, (TimeSpan)arg, From);
			token.Source = this;
			return token;
		}

		void ICandleSource.UnRegister(CandleToken token)
		{
			Trader.UnRegisterHistoryRealTimeCandles(token);
		}

		private static void CheckTypes(Type candleType, object arg)
		{
			if (candleType == null)
				throw new ArgumentNullException("candleType");

			if (candleType != typeof(TimeFrameCandle))
				throw new ArgumentException("SmartCOM поддерживает свечки только типа {0}.".Put(candleType), "candleType");

			if (!(arg is TimeSpan))
				throw new ArgumentException("Аргумент {0} должен быть типа {1}.".Put(arg, typeof(TimeSpan)), "arg");
		}

		private void OnNewHistoryCandles(CandleToken token, IEnumerable<TimeFrameCandle> candles)
		{
			_candlesStarted.SafeInvoke(token, candles);
			_candlesFinished.SafeInvoke(token, candles);
		}

		void IDisposable.Dispose()
		{
			Trader.NewHistoryCandles -= OnNewHistoryCandles;
		}
	}

Topic starter
Thanks:

Garic

Avatar
Date: 12/27/2011
Reply


Спасибо, заработало RollEyes

Правда пока криво и результаты немного расходятся с обычным тестом на истории - как доковыряю - выложу.

Нужно синхронизировать поток свечей и поток NewTrade чтобы сделки проходили одинаково при обоих тестах (тест по расчитанным свечам и тест по сделкам).
Т.е. нужно выдавать свечи по какому-то событию. Логично чтобы это был Trader.NewTrade. Но как определить по tradе нужно ли выплёвывать candleFinished для свечки? Для timeFrame свечей понятно, а для остальных - такой информации нет.

Похоже облом - нужно чтобы у сохранённой свечи в хранилище был код первой и последней сделки (или есть другой вариант? )

В моих кастомных range-барах это есть (для отладки сделал), но что делать со стандартными?


По скорости - тест недели RIZ1 стратегия на минутках - Покупка на 00 минуте, продажа на 30 минуте по candleFinished
- по сделкам 02:07.10
- по сохранённым свечам, синхронизация по Trader.MarketTimeChanged раз в минуту 00:21.96
- по сохранённым свечам, синхронизация по Trader.MarketTimeChanged раз в секунду 00:22.77
- по сохранённым свечам, синхронизация по Trader.NewTrade 00:24.15

примерно в 7 раз быстрее
Thanks:

Garic

Avatar
Date: 1/28/2012
Reply


Дошли наконец-то руки :)

Сделал пример для TimeFrameCandles
Скорость примерно в 5.5 раза выше чем с CandleBuilder (в примере можно переключать)

Данные совпадают, за исключением свечей NonTradingDealType которые почему-то стандартный CandleBuilder выдаёт. Пример 06.10.2011 - свеча 14.00 - её быть не должно. Параметр CandleManager.NonTradingDealType не влияет.
Thanks:

Mikhail Sukhov

Avatar
Articles author Programmer Trader
Date: 1/28/2012
Reply


Garic Перейти
Дошли наконец-то руки :)


В ПО очень важно быть на онлайне постоянно. С тех пор и стратегия стала событийной, и S# 4.1 (что в dev ветке) поддерживает закачку свечек из хранилища.
Topic starter
Thanks:

Spiritschaser

Avatar
Date: 4/24/2012
Reply


Помогите. Программирую я плохо, как мифический индус. Пишу оптимизатор, хочу сделать тестирование на готовых свечках из текстовых таблиц. С чего нужно начать? Сделать свой источник или Builder?
Thanks:
< 1 2 3 

Attach files by dragging & dropping, , or pasting from the clipboard.

loading
clippy