Перфоманс тестинга на истории
Atom
22.08.2011
Mikhail Sukhov


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

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

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

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

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



Спасибо:


<< < 5 6 7 
Mikhail Sukhov

Фотография
Дата: 27.12.2011
Ответить


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;
		}
	}

Спасибо:

Garic

Фотография
Дата: 28.12.2011
Ответить


Спасибо, заработало [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 раз быстрее
Спасибо:

Garic

Фотография
Дата: 28.01.2012
Ответить


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

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

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

Mikhail Sukhov

Фотография
Дата: 28.01.2012
Ответить


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


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

Spiritschaser

Фотография
Дата: 24.04.2012
Ответить


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

Добавить файлы через драг-н-дроп, , или вставить из буфера обмена.

loading
clippy