static void Main(string[] args) { _candleManager = new CandleManager(); if (!GetSetings()) return; var storageRegistry = new StorageRegistry(); ((LocalMarketDataDrive)storageRegistry.DefaultDrive).Path = _historyPath; var cbs = new TradeStorageCandleBuilderSource { StorageRegistry = storageRegistry }; _candleManager.Sources.OfType<TimeFrameCandleBuilder>().Single().Sources.Add(cbs); _candleManager.Processing += GetCandles; foreach (var Sec in listOfName) { foreach (var timeFrame in listOfTimeFrame) { _series = new CandleSeries(typeof(TimeFrameCandle), Sec, timeFrame); LisfStreamWriters.Add(_series.ToString(), new StreamWriter(GetFileName(_series), false)); _candleManager.Start(_series, _startTime, _endTime); } } Console.ReadKey(); // Закроем все потоки которые мы записывали foreach (var strim in LisfStreamWriters) { strim.Value.Close(); } }
public EMAStrategyOptimizer(Security security, StorageRegistry storage, Portfolio portfolio, DateTime startTime, DateTime stopTime) { _startTime = startTime; _stopTime = stopTime; _security = security; _portfolio = portfolio; _storage = storage; this.Volume = 1; this.UseQuoting = true; }
static void Main() { // создаем тестовый инструмент var security = new Security { Id = "TestId", PriceStep = 0.1m, Decimals = 1, }; var trades = new List<Trade>(); // генерируем 1000 произвольных сделок // for (var i = 0; i < 1000; i++) { var t = new Trade { Time = DateTime.Today + TimeSpan.FromMinutes(i), Id = i + 1, Security = security, Volume = RandomGen.GetInt(1, 10), Price = RandomGen.GetInt(1, 100) * security.PriceStep ?? 1m + 99 }; trades.Add(t); } var storage = new StorageRegistry(); // получаем хранилище для тиковых сделок var tradeStorage = storage.GetTradeStorage(security); // сохраняем сделки tradeStorage.Save(trades); // загружаем сделки var loadedTrades = tradeStorage.Load(DateTime.Today, DateTime.Today + TimeSpan.FromMinutes(1000)); foreach (var trade in loadedTrades) { Console.WriteLine(LocalizedStrings.Str2968Params, trade.Id, trade); } Console.ReadLine(); // удаляем сделки (очищаем файл) tradeStorage.Delete(DateTime.Today, DateTime.Today + TimeSpan.FromMinutes(1000)); }
public MainWindow() { InitializeComponent(); this.LayoutManager = new LayoutManager(this, ProgrammaticDockSite); LayoutManager._layoutFile = Path.Combine(_settingsFolder, "layout.xml"); ConnectCommand = new DelegateCommand(Connect, CanConnect); SettingsCommand = new DelegateCommand(Settings, CanSettings); Directory.CreateDirectory(_settingsFolder); var storageRegistry = new StorageRegistry {DefaultDrive = new LocalMarketDataDrive(_settingsFolder)}; var securityStorage = storageRegistry.GetSecurityStorage(); Connector = new Connector {EntityFactory = new StorageEntityFactory(securityStorage)}; ConfigManager.RegisterService(new FilterableSecurityProvider(securityStorage)); ConfigManager.RegisterService<IConnector>(Connector); ConfigManager.RegisterService<IMarketDataProvider>(Connector); _connectionFile = Path.Combine(_settingsFolder, "connection.xml"); if (File.Exists(_connectionFile)) Connector.Adapter.Load(new XmlSerializer<SettingsStorage>().Deserialize(_connectionFile)); _secView = new SecuritiesView(this) {SecurityGrid = {MarketDataProvider = Connector}}; Connector.NewSecurities += _secView.SecurityGrid.Securities.AddRange; Connector.MarketDepthsChanged += depths => { foreach (var depth in depths) { var ctrl = _depths.TryGetValue(depth.Security); if (ctrl != null) ctrl.UpdateDepth(depth); } }; }
public KeyValuePair<OptVarItem, EMAEventModelStrategy> GetOptContext(OptVarItem optVarItem) { // clone doesn't work for some reason var security = new Security { Id = _security.Id, Code = _security.Code, Name = _security.Name, MinStepSize = _security.MinStepSize, MinStepPrice = _security.MinStepPrice, ExchangeBoard = _security.ExchangeBoard, MaxPrice = 99999, MinPrice = 1 }; // Create local Storage to make it disposable after optimization var storage = new StorageRegistry(); ((LocalMarketDataDrive) storage.DefaultDrive).Path = ((LocalMarketDataDrive) _storage.DefaultDrive).Path; ((LocalMarketDataDrive) storage.DefaultDrive).UseAlphabeticPath = true; var portfolio = new Portfolio { BeginValue = _portfolio.BeginValue }; EmulationTrader trader = new EmulationTrader( new[] { security }, new[] { portfolio }) { MarketTimeChangedInterval = optVarItem.TimeFrame, StorageRegistry = storage, UseMarketDepth = true, //UseCandlesTimeFrame = optVarItem.TimeFrame }; if (trader.UseMarketDepth) { trader.MarketEmulator.Settings.DepthExpirationTime = TimeSpan.FromMinutes(5); // Default: TimeSpan.FromDays(1); var marketDepthGenerator = new TrendMarketDepthGenerator(security) { // стакан для инструмента в истории обновляется раз в 10 секунд Interval = TimeSpan.FromSeconds(10), //MaxAsksDepth = 5, //MaxBidsDepth = 5 }; trader.RegisterMarketDepth(marketDepthGenerator); trader.StateChanged += (oldState, newState) => { if (trader.State == EmulationStates.Stopped) { trader.UnRegisterMarketDepth(marketDepthGenerator); marketDepthGenerator = null; } }; } // соединяемся с трейдером и запускаем экспорт, // чтобы инициализировать переданными инструментами и портфелями необходимые свойства EmulationTrader trader.Connect(); trader.StartExport(); var series = new CandleSeries(typeof(TimeFrameCandle), trader.Securities.First(), optVarItem.TimeFrame); var candleManager = new CandleManager(trader); candleManager.Start(series); var strategy = new EMAEventModelStrategy(series, new ExponentialMovingAverage { Length = optVarItem.FilterOptPeriod }, new ExponentialMovingAverage { Length = optVarItem.LongOptPeriods }, new ExponentialMovingAverage { Length = optVarItem.ShortOptPeriods }, optVarItem.TakeProfitUnit, optVarItem.StopLossUnit) { Volume = this.Volume, Portfolio = portfolio, Security = security, Trader = trader, UseQuoting = this.UseQuoting }; trader.StateChanged += (oldState, newState) => { if (trader.State == EmulationStates.Started) { strategy.Start(); } else if (trader.State == EmulationStates.Stopped) { strategy.Stop(); candleManager = null; storage = null; } }; var result = new KeyValuePair<OptVarItem, EMAEventModelStrategy>(optVarItem, strategy); return result; }
private void StartBtnClick(object sender, RoutedEventArgs e) { InitChart(); if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (_connectors.Any(t => t.State != EmulationStates.Stopped)) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var secIdParts = SecId.Text.Split('@'); if (secIdParts.Length != 2) { MessageBox.Show(this, LocalizedStrings.Str3016); return; } var timeFrame = TimeSpan.FromMinutes(5); // создаем настройки для тестирования var settings = new[] { Tuple.Create( TicksCheckBox, TicksTestingProcess, TicksParameterGrid, // тест только на тиках new EmulationInfo { CurveColor = Colors.DarkGreen, StrategyName = LocalizedStrings.Str3017 }), Tuple.Create( TicksAndDepthsCheckBox, TicksAndDepthsTestingProcess, TicksAndDepthsParameterGrid, // тест на тиках + стаканы new EmulationInfo { UseMarketDepth = true, CurveColor = Colors.Red, StrategyName = LocalizedStrings.Str3018 }), Tuple.Create( CandlesCheckBox, CandlesTestingProcess, CandlesParameterGrid, // тест на свечах new EmulationInfo { UseCandleTimeFrame = timeFrame, CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Str3019 }), Tuple.Create( CandlesAndDepthsCheckBox, CandlesAndDepthsTestingProcess, CandlesAndDepthsParameterGrid, // тест на свечах + стаканы new EmulationInfo { UseMarketDepth = true, UseCandleTimeFrame = timeFrame, CurveColor = Colors.Cyan, StrategyName = LocalizedStrings.Str3020 }), Tuple.Create( OrderLogCheckBox, OrderLogTestingProcess, OrderLogParameterGrid, // тест на логе заявок new EmulationInfo { UseOrderLog = true, CurveColor = Colors.CornflowerBlue, StrategyName = LocalizedStrings.Str3021 }) }; // хранилище, через которое будет производиться доступ к тиковой и котировочной базе var storageRegistry = new StorageRegistry { // изменяем путь, используемый по умолчанию DefaultDrive = new LocalMarketDataDrive(HistoryPath.Text) }; var startTime = (DateTime)From.Value; var stopTime = (DateTime)To.Value; // ОЛ необходимо загружать с 18.45 пред дня, чтобы стаканы строились правильно if (OrderLogCheckBox.IsChecked == true) { startTime = startTime.Subtract(TimeSpan.FromDays(1)).AddHours(18).AddMinutes(45).AddTicks(1); } // задаем шаг ProgressBar var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // в реальности период может быть другим, и это зависит от объема данных, // хранящихся по пути HistoryPath, TicksTestingProcess.Maximum = TicksAndDepthsTestingProcess.Maximum = CandlesTestingProcess.Maximum = 100; TicksTestingProcess.Value = TicksAndDepthsTestingProcess.Value = CandlesTestingProcess.Value = 0; var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // чтобы смотреть логи в отладчике - работает медленно. var generateDepths = GenDepthsCheckBox.IsChecked == true; var maxDepth = MaxDepth.Text.To <int>(); var maxVolume = MaxVolume.Text.To <int>(); var secCode = secIdParts[0]; var board = ExchangeBoard.GetOrCreateBoard(secIdParts[1]); foreach (var set in settings) { if (set.Item1.IsChecked == false) { continue; } var progressBar = set.Item2; var statistic = set.Item3; var emulationInfo = set.Item4; // создаем тестовый инструмент, на котором будет производится тестирование var security = new Security { Id = SecId.Text, // по идентификатору инструмента будет искаться папка с историческими маркет данными Code = secCode, Board = board, }; var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // тестовый портфель var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // создаем подключение для эмуляции // инициализируем настройки (инструмент в истории обновляется раз в секунду) var connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { StorageRegistry = storageRegistry, MarketEmulator = { Settings = { // использовать свечи UseCandlesTimeFrame = emulationInfo.UseCandleTimeFrame, // сведение сделки в эмуляторе если цена коснулась нашей лимитной заявки. // Если выключено - требуется "прохождение цены сквозь уровень" // (более "суровый" режим тестирования.) MatchOnTouch = false, } }, //UseExternalCandleSource = true, CreateDepthFromOrdersLog = emulationInfo.UseOrderLog, CreateTradesFromOrdersLog = emulationInfo.UseOrderLog, }; connector.MarketDataAdapter.SessionHolder.MarketTimeChangedInterval = timeFrame; ((ILogSource)connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; logManager.Sources.Add(connector); connector.NewSecurities += securities => { //подписываемся на получение данных после получения инструмента if (securities.All(s => s != security)) { return; } // отправляем данные Level1 для инструмента connector.MarketDataAdapter.SendOutMessage(level1Info); // тест подразумевает наличие стаканов if (emulationInfo.UseMarketDepth) { connector.RegisterMarketDepth(security); if ( // если выбрана генерация стаканов вместо реальных стаканов generateDepths || // для свечей генерируем стаканы всегда emulationInfo.UseCandleTimeFrame != TimeSpan.Zero ) { // если история по стаканам отсутствует, но стаканы необходимы для стратегии, // то их можно сгенерировать на основании цен последних сделок или свечек. connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromSeconds(1), // стакан для инструмента в истории обновляется раз в секунду MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 2, // минимальный генерируемый спред - 2 минимальных шага цены MaxSpreadStepCount = 5, // не генерировать спрэд между лучшим бид и аск больше чем 5 минимальных шагов цены - нужно чтобы при генерации из свечей не получалось слишком широкого спреда. MaxPriceStepCount = 3 // максимальное количество шагов между ценами, }); } } else if (emulationInfo.UseOrderLog) { connector.RegisterOrderLog(security); } }; // соединяемся с трейдером и запускаем экспорт, // чтобы инициализировать переданными инструментами и портфелями необходимые свойства EmulationTrader connector.Connect(); connector.StartExport(); var candleManager = new CandleManager(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); _shortMa = new SimpleMovingAverage { Length = 10 }; _shortElem = new ChartIndicatorElement { Color = Colors.Coral, ShowAxisMarker = false, FullTitle = _shortMa.ToString() }; _bufferedChart.AddElement(_area, _shortElem); _longMa = new SimpleMovingAverage { Length = 80 }; _longElem = new ChartIndicatorElement { ShowAxisMarker = false, FullTitle = _longMa.ToString() }; _bufferedChart.AddElement(_area, _longElem); // создаем торговую стратегию, скользящие средние на 80 5-минуток и 10 5-минуток var strategy = new SmaStrategy(_bufferedChart, _candlesElem, _tradesElem, _shortMa, _shortElem, _longMa, _longElem, series) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // по-умолчанию интервал равен 1 минут, // что для истории в диапазон от нескольких месяцев излишне UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; // комиссия в 1 копейку за сделку connector.MarketEmulator.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); logManager.Sources.Add(strategy); // копируем параметры на визуальную панель statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var pnlCurve = Curve.CreateCurve("P&L " + emulationInfo.StrategyName, emulationInfo.CurveColor, EquityCurveChartStyles.Area); var unrealizedPnLCurve = Curve.CreateCurve(LocalizedStrings.PnLUnreal + emulationInfo.StrategyName, Colors.Black); var commissionCurve = Curve.CreateCurve(LocalizedStrings.Str159 + " " + emulationInfo.StrategyName, Colors.Red, EquityCurveChartStyles.DashedLine); var posItems = PositionCurve.CreateCurve(emulationInfo.StrategyName, emulationInfo.CurveColor); strategy.PnLChanged += () => { var pnl = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL }; var commission = new EquityData { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); var nextTime = startTime + progressStep; // и подписываемся на событие изменения времени, чтобы обновить ProgressBar connector.MarketTimeChanged += d => { if (connector.CurrentTime < nextTime && connector.CurrentTime < stopTime) { return; } var steps = (connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => progressBar.Value = steps); }; connector.StateChanged += () => { if (connector.State == EmulationStates.Stopped) { candleManager.Stop(series); strategy.Stop(); logManager.Dispose(); _connectors.Clear(); SetIsEnabled(false); this.GuiAsync(() => { if (connector.IsFinished) { progressBar.Value = progressBar.Maximum; MessageBox.Show(LocalizedStrings.Str3024 + (DateTime.Now - _startEmulationTime)); } else { MessageBox.Show(LocalizedStrings.cancelled); } }); } else if (connector.State == EmulationStates.Started) { SetIsEnabled(true); // запускаем стратегию когда эмулятор запустился strategy.Start(); candleManager.Start(series); } }; if (ShowDepth.IsChecked == true) { MarketDepth.UpdateFormat(security); connector.NewMessage += (message, dir) => { var quoteMsg = message as QuoteChangeMessage; if (quoteMsg != null) { MarketDepth.UpdateDepth(quoteMsg); } }; } _connectors.Add(connector); } _startEmulationTime = DateTime.Now; // запускаем эмуляцию foreach (var connector in _connectors) { // указываем даты начала и конца тестирования connector.Start(startTime, stopTime); } TabControl.Items.Cast <TabItem>().First(i => i.Visibility == Visibility.Visible).IsSelected = true; }
private void StartEmulation() { if (_connector != null && _connector.State != EmulationStates.Stopped) throw new InvalidOperationException(LocalizedStrings.Str3015); if (Strategy == null) throw new InvalidOperationException("Strategy not selected."); var strategy = (EmulationDiagramStrategy)Strategy; if (strategy.DataPath.IsEmpty() || !Directory.Exists(strategy.DataPath)) throw new InvalidOperationException(LocalizedStrings.Str3014); strategy .Composition .Parameters .ForEach(p => { if (p.Type == typeof(Security) && p.Value == null) throw new InvalidOperationException(LocalizedStrings.Str1380); }); strategy.Reset(); Reset(); var securityId = "empty@empty"; var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(securityId); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = strategy.CandlesTimeFrame; var useCandles = strategy.MarketDataSource == MarketDataSource.Candles; // create test security var security = new Security { Id = securityId, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(strategy.DataPath) }; var startTime = strategy.StartDate.ChangeKind(DateTimeKind.Utc); var stopTime = strategy.StopDate.ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; var securityProvider = ConfigManager.GetService<ISecurityProvider>(); // create backtesting connector _connector = new HistoryEmulationConnector(securityProvider, new[] { portfolio }, new StorageRegistry()) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; //((ILogSource)_connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; ConfigManager.GetService<LogManager>().Sources.Add(_connector); var candleManager = new CandleManager(_connector); strategy.Volume = 1; strategy.Portfolio = portfolio; strategy.Security = security; strategy.Connector = _connector; //LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months strategy.UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>(); strategy.SetCandleManager(candleManager); _connector.NewSecurity += s => { var level1Info = new Level1ChangeMessage { SecurityId = s.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // fill level1 values _connector.SendInMessage(level1Info); //_connector.RegisterMarketDepth(security); //if (!useCandles) // _connector.RegisterTrades(s); }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) return; var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To<TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.LookupSecuritiesResult += (ss) => { if (strategy.ProcessState != ProcessStates.Stopped) return; // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); this.GuiAsync(() => { if (_connector.IsFinished) TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; }); break; case EmulationStates.Started: break; } }; TicksAndDepthsProgress.Value = 0; DiagramDebuggerControl.Debugger.IsEnabled = true; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
private void StartBtnClick(object sender, RoutedEventArgs e) { InitChart(); if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (_connectors.Any(t => t.State != EmulationStates.Stopped)) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var secIdParts = SecId.Text.Split('@'); if (secIdParts.Length != 2) { MessageBox.Show(this, LocalizedStrings.Str3016); return; } var timeFrame = TimeSpan.FromMinutes(5); // create backtesting modes var settings = new[] { Tuple.Create( TicksCheckBox, TicksTestingProcess, TicksParameterGrid, // ticks new EmulationInfo {UseTicks = true, CurveColor = Colors.DarkGreen, StrategyName = LocalizedStrings.Ticks}), Tuple.Create( TicksAndDepthsCheckBox, TicksAndDepthsTestingProcess, TicksAndDepthsParameterGrid, // ticks + order book new EmulationInfo {UseTicks = true, UseMarketDepth = true, CurveColor = Colors.Red, StrategyName = LocalizedStrings.XamlStr757}), Tuple.Create( DepthsCheckBox, DepthsTestingProcess, DepthsParameterGrid, // order book new EmulationInfo {UseMarketDepth = true, CurveColor = Colors.OrangeRed, StrategyName = LocalizedStrings.MarketDepths}), Tuple.Create( CandlesCheckBox, CandlesTestingProcess, CandlesParameterGrid, // candles new EmulationInfo {UseCandleTimeFrame = timeFrame, CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Candles}), Tuple.Create( CandlesAndDepthsCheckBox, CandlesAndDepthsTestingProcess, CandlesAndDepthsParameterGrid, // candles + orderbook new EmulationInfo {UseMarketDepth = true, UseCandleTimeFrame = timeFrame, CurveColor = Colors.Cyan, StrategyName = LocalizedStrings.XamlStr635}), Tuple.Create( OrderLogCheckBox, OrderLogTestingProcess, OrderLogParameterGrid, // order log new EmulationInfo {UseOrderLog = true, CurveColor = Colors.CornflowerBlue, StrategyName = LocalizedStrings.OrderLog}) }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Text) }; var startTime = ((DateTime)From.Value).ChangeKind(DateTimeKind.Utc); var stopTime = ((DateTime)To.Value).ChangeKind(DateTimeKind.Utc); // ОЛ необходимо загружать с 18.45 пред дня, чтобы стаканы строились правильно if (OrderLogCheckBox.IsChecked == true) startTime = startTime.Subtract(TimeSpan.FromDays(1)).AddHours(18).AddMinutes(45).AddTicks(1); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); // set ProgressBar bounds _progressBars.ForEach(p => { p.Value = 0; p.Maximum = 100; }); var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // for track logs in output window in Vusial Studio (poor performance). var generateDepths = GenDepthsCheckBox.IsChecked == true; var maxDepth = MaxDepth.Text.To<int>(); var maxVolume = MaxVolume.Text.To<int>(); var secCode = secIdParts[0]; var board = ExchangeBoard.GetOrCreateBoard(secIdParts[1]); foreach (var set in settings) { if (set.Item1.IsChecked == false) continue; var progressBar = set.Item2; var statistic = set.Item3; var emulationInfo = set.Item4; // create test security var security = new Security { Id = SecId.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector var connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { MarketEmulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } }, UseExternalCandleSource = emulationInfo.UseCandleTimeFrame != null, CreateDepthFromOrdersLog = emulationInfo.UseOrderLog, CreateTradesFromOrdersLog = emulationInfo.UseOrderLog, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; logManager.Sources.Add(connector); var candleManager = emulationInfo.UseCandleTimeFrame == null ? new CandleManager(new TradeCandleBuilderSourceEx(connector)) : new CandleManager(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); _shortMa = new SimpleMovingAverage { Length = 10 }; _shortElem = new ChartIndicatorElement { Color = Colors.Coral, ShowAxisMarker = false, FullTitle = _shortMa.ToString() }; _bufferedChart.AddElement(_area, _shortElem); _longMa = new SimpleMovingAverage { Length = 80 }; _longElem = new ChartIndicatorElement { ShowAxisMarker = false, FullTitle = _longMa.ToString() }; _bufferedChart.AddElement(_area, _longElem); // create strategy based on 80 5-min и 10 5-min var strategy = new SmaStrategy(_bufferedChart, _candlesElem, _tradesElem, _shortMa, _shortElem, _longMa, _longElem, series) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>() }; logManager.Sources.Add(strategy); connector.NewSecurities += securities => { if (securities.All(s => s != security)) return; // fill level1 values connector.SendInMessage(level1Info); if (emulationInfo.UseMarketDepth) { connector.RegisterMarketDepth(security); if ( // if order book will be generated generateDepths || // of backtesting will be on candles emulationInfo.UseCandleTimeFrame != TimeSpan.Zero ) { // if no have order book historical data, but strategy is required, // use generator based on last prices connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } if (emulationInfo.UseOrderLog) { connector.RegisterOrderLog(security); } if (emulationInfo.UseTicks) { connector.RegisterTrades(security); } // start strategy before emulation started strategy.Start(); candleManager.Start(series); // start historical data loading when connection established successfully and all data subscribed connector.Start(); }; // fill parameters panel statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var pnlCurve = Curve.CreateCurve("P&L " + emulationInfo.StrategyName, emulationInfo.CurveColor, EquityCurveChartStyles.Area); var unrealizedPnLCurve = Curve.CreateCurve(LocalizedStrings.PnLUnreal + emulationInfo.StrategyName, Colors.Black); var commissionCurve = Curve.CreateCurve(LocalizedStrings.Str159 + " " + emulationInfo.StrategyName, Colors.Red, EquityCurveChartStyles.DashedLine); var posItems = PositionCurve.CreateCurve(emulationInfo.StrategyName, emulationInfo.CurveColor); strategy.PnLChanged += () => { var pnl = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL }; var commission = new EquityData { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); var nextTime = startTime + progressStep; // handle historical time for update ProgressBar connector.MarketTimeChanged += d => { if (connector.CurrentTime < nextTime && connector.CurrentTime < stopTime) return; var steps = (connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To<TimeSpan>(); this.GuiAsync(() => progressBar.Value = steps); }; connector.StateChanged += () => { if (connector.State == EmulationStates.Stopped) { candleManager.Stop(series); strategy.Stop(); logManager.Dispose(); _connectors.Clear(); SetIsEnabled(false); this.GuiAsync(() => { if (connector.IsFinished) { progressBar.Value = progressBar.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else MessageBox.Show(this, LocalizedStrings.cancelled); }); } else if (connector.State == EmulationStates.Started) { SetIsEnabled(true); } }; if (ShowDepth.IsChecked == true) { MarketDepth.UpdateFormat(security); connector.NewMessage += message => { var quoteMsg = message as QuoteChangeMessage; if (quoteMsg != null) MarketDepth.UpdateDepth(quoteMsg); }; } _connectors.Add(connector); progressBar.Value = 0; } _startEmulationTime = DateTime.Now; // start emulation foreach (var connector in _connectors) { // raise NewSecurities and NewPortfolio for full fill strategy properties connector.Connect(); // 1 cent commission for trade connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); } TabControl.Items.Cast<TabItem>().First(i => i.Visibility == Visibility.Visible).IsSelected = true; }
private void OnHistoryStartClick(object sender, RoutedEventArgs e) { btnHistoryStart.IsEnabled = false; this.InitGrids(); // создаем тестовый инструмент, на котором будет производится тестирование var security = new Security { Id = this.txtSecurityId.Text, // по идентификатору инструмента будет искаться папка с историческими маркет данными Code = this.txtSecurityCode.Text, Name = this.txtSecurityCode.Text, MinPrice = 1, MaxPrice = 99999, MinStepSize = 1, MinStepPrice = 1, ExchangeBoard = ExchangeBoard.Forts, }; security.ExchangeBoard.IsSupportAtomicReRegister = false; // fixed quoting reregister error var storageRegistry = new StorageRegistry(); ((LocalMarketDataDrive) storageRegistry.DefaultDrive).Path = this.txtHistoryPath.Text; ((LocalMarketDataDrive) storageRegistry.DefaultDrive).UseAlphabeticPath = true; var portfolio = new Portfolio { Name = "test account", BeginValue = 30000m }; DateTime startTime; DateTime stopTime; if (!DateTime.TryParse(txtHistoryRangeEnd.Text, out stopTime)) { stopTime = DateTime.Now; txtHistoryRangeEnd.Text = stopTime.ToString(); } if (!DateTime.TryParse(txtHistoryRangeBegin.Text, out startTime)) { startTime = stopTime.AddDays(-3); txtHistoryRangeBegin.Text = startTime.ToString(); } EMAStrategyOptimizer optimizer = new EMAStrategyOptimizer(security, storageRegistry, portfolio, startTime, stopTime) { Volume = this.Volume, Log = _log }; var context = optimizer.GetOptContext(this.MainOptVarItem); _trader = context.Value.Trader; _strategy = context.Value; this.InitChart(_strategy); _strategy.Trader.NewOrders += orders => orders.ForEach(OnOrderRegistered); _strategy.Trader.NewMyTrades += OnNewTrades; _logManager.Sources.Add(_strategy); // устанавливаем в визуальный элемент ProgressBar максимальное количество итераций) this.pbHistoryTestProgress.Maximum = 10; this.pbHistoryTestProgress.Value = 0; var totalMinutes = (stopTime - startTime).TotalMinutes; var segment = Math.Floor(totalMinutes / 10); var nSegment = 1; var sSegment = segment; _trader.MarketTimeChanged += span => { var currentMinute = (_trader.CurrentTime - startTime).TotalMinutes; if (currentMinute >= sSegment || _trader.CurrentTime >= stopTime) { nSegment += 1; sSegment = segment * nSegment; this.GuiAsync(() => { this.pbHistoryTestProgress.Value = nSegment; this.UpdateStrategyStat(_strategy); }); } }; Stopwatch sw = new Stopwatch(); ((EmulationTrader)_trader).StateChanged += (states, emulationStates) => { if (((EmulationTrader)_trader).State == EmulationStates.Stopped) { sw.Stop(); this.GuiAsync(() => { this.UpdateStrategyStat(_strategy); _strategy.CandleSeries.GetCandles<TimeFrameCandle>().ForEach(DrawCandleAndEma); _strategy.Trader.MyTrades.ForEach(DrawTrade); this.pbHistoryTestProgress.Value = 0; btnHistoryStart.IsEnabled = true; }); // clean stupid dictionary //var value = _trader.GetType().GetField("#=qUTBJ0c9uFmGWYx4a3_oZjOoV9pJDtArCh9oL5k$U8DQ=", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_trader); //value.GetType().GetMethod("Clear").Invoke(value, null); _log.AddLog(new LogMessage(_log, DateTime.Now, LogLevels.Info, String.Format("History testing done ({0}). Result: PnL: {1}, {2}", sw.Elapsed, _strategy.PnLManager.PnL, this.MainOptVarItem ))); } else if (((EmulationTrader)_trader).State == EmulationStates.Started) { sw.Start(); } }; // соединяемся с трейдером и запускаем экспорт, // чтобы инициализировать переданными инструментами и портфелями необходимые свойства EmulationTrader _trader.Connect(); _trader.StartExport(); // запускаем эмуляцию, задавая период тестирования (startTime, stopTime). ((EmulationTrader)_trader).Start(startTime, stopTime); }
protected override TimeSpan OnProcess() { // если фильтр по инструментам выключен (выбран инструмент все инструменты) if (this.GetAllSecurity() != null) { //throw new InvalidOperationException("Источник не поддерживает закачку данных по всем инструментам."); this.AddWarningLog(LocalizedStrings.Str2292); return(TimeSpan.MaxValue); } var source = new AlorHistorySource(); if (_settings.UseTemporaryFiles != TempFiles.NotUse) { source.DumpFolder = GetTempPath(); } var startDate = _settings.StartFrom; var endDate = DateTime.Today - TimeSpan.FromDays(_settings.Offset); var allDates = startDate.Range(endDate, TimeSpan.FromDays(1)).ToArray(); foreach (var security in GetWorkingSecurities()) { if (!CanProcess()) { break; } #region LoadCandles if (security.CandleSeries.Any()) { foreach (var series in security.CandleSeries) { if (!CanProcess()) { break; } if (series.CandleType != typeof(TimeFrameCandle)) { this.AddWarningLog(LocalizedStrings.Str2296Params, series); continue; } var storage = StorageRegistry.GetCandleStorage(series.CandleType, security.Security, series.Arg, _settings.Drive, _settings.StorageFormat); var emptyDates = allDates.Except(storage.Dates).ToArray(); if (emptyDates.IsEmpty()) { this.AddInfoLog(LocalizedStrings.Str2297Params, series.Arg, security.Security.Id); continue; } foreach (var emptyDate in emptyDates) { if (!CanProcess()) { break; } if (_settings.IgnoreWeekends && !security.IsTradeDate(emptyDate)) { this.AddDebugLog(LocalizedStrings.WeekEndDate, emptyDate); continue; } try { this.AddInfoLog(LocalizedStrings.Str2298Params, series.Arg, emptyDate, security.Security.Id); var candles = source.GetCandles(security.Security, (TimeSpan)series.Arg, emptyDate, emptyDate); if (candles.Any()) { SaveCandles(security, candles); } else { this.AddDebugLog(LocalizedStrings.NoData); } if (_settings.UseTemporaryFiles == TempFiles.UseAndDelete) { File.Delete(source.GetDumpFile(security.Security, emptyDate, emptyDate, typeof(TimeFrameCandle), series.Arg)); } } catch (Exception ex) { HandleError(new InvalidOperationException(LocalizedStrings.Str2299Params .Put(series.Arg, emptyDate, security.Security.Id), ex)); } } } } #endregion } if (CanProcess()) { this.AddInfoLog(LocalizedStrings.Str2300); } return(base.OnProcess()); }
private void StartBtnClick(object sender, RoutedEventArgs e) { if (_connectors.Count > 0) { foreach (var connector in _connectors.Cache) { connector.Start(); } return; } if (HistoryPath.Folder.IsEmpty() || !Directory.Exists(HistoryPath.Folder)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (_connectors.Any(t => t.State != EmulationStates.Stopped)) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var id = SecId.Text.ToSecurityId(); //if (secIdParts.Length != 2) //{ // MessageBox.Show(this, LocalizedStrings.Str3016); // return; //} var timeFrame = TimeSpan.FromMinutes(TimeFrame.SelectedIndex == 0 ? 1 : 5); var secCode = id.SecurityCode; var board = _exchangeInfoProvider.GetOrCreateBoard(id.BoardCode); // create test security var security = new Security { Id = SecId.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // create backtesting modes var settings = new[] { Tuple.Create( TicksCheckBox, TicksProgress, TicksParameterGrid, // ticks new EmulationInfo { UseTicks = true, CurveColor = Colors.DarkGreen, StrategyName = LocalizedStrings.Ticks }, TicksChart, TicksEquity, TicksPosition), Tuple.Create( TicksAndDepthsCheckBox, TicksAndDepthsProgress, TicksAndDepthsParameterGrid, // ticks + order book new EmulationInfo { UseTicks = true, UseMarketDepth = true, CurveColor = Colors.Red, StrategyName = LocalizedStrings.XamlStr757 }, TicksAndDepthsChart, TicksAndDepthsEquity, TicksAndDepthsPosition), Tuple.Create( DepthsCheckBox, DepthsProgress, DepthsParameterGrid, // order book new EmulationInfo { UseMarketDepth = true, CurveColor = Colors.OrangeRed, StrategyName = LocalizedStrings.MarketDepths }, DepthsChart, DepthsEquity, DepthsPosition), Tuple.Create( CandlesCheckBox, CandlesProgress, CandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Candles }, CandlesChart, CandlesEquity, CandlesPosition), Tuple.Create( CandlesAndDepthsCheckBox, CandlesAndDepthsProgress, CandlesAndDepthsParameterGrid, // candles + orderbook new EmulationInfo { UseMarketDepth = true, UseCandleTimeFrame = timeFrame, CurveColor = Colors.Cyan, StrategyName = LocalizedStrings.XamlStr635 }, CandlesAndDepthsChart, CandlesAndDepthsEquity, CandlesAndDepthsPosition), Tuple.Create( OrderLogCheckBox, OrderLogProgress, OrderLogParameterGrid, // order log new EmulationInfo { UseOrderLog = true, CurveColor = Colors.CornflowerBlue, StrategyName = LocalizedStrings.OrderLog }, OrderLogChart, OrderLogEquity, OrderLogPosition), Tuple.Create( Level1CheckBox, Level1Progress, Level1ParameterGrid, // order log new EmulationInfo { UseLevel1 = true, CurveColor = Colors.Aquamarine, StrategyName = LocalizedStrings.Level1 }, Level1Chart, Level1Equity, Level1Position), Tuple.Create( FinamCandlesCheckBox, FinamCandlesProgress, FinamCandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CustomHistoryAdapter = g => new FinamMessageAdapter(g), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.FinamCandles }, FinamCandlesChart, FinamCandlesEquity, FinamCandlesPosition), Tuple.Create( YahooCandlesCheckBox, YahooCandlesProgress, YahooCandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CustomHistoryAdapter = g => new YahooMessageAdapter(g), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.YahooCandles }, YahooCandlesChart, YahooCandlesEquity, YahooCandlesPosition), Tuple.Create( RandomCheckBox, RandomProgress, RandomParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CustomHistoryAdapter = g => new OwnMessageAdapter(g), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Custom }, RandomChart, RandomEquity, RandomPosition), }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Folder) }; var startTime = ((DateTime)From.EditValue).UtcKind(); var stopTime = ((DateTime)To.EditValue).UtcKind(); // (ru only) ОЛ необходимо загружать с 18.45 пред дня, чтобы стаканы строились правильно if (OrderLogCheckBox.IsChecked == true) { startTime = startTime.Subtract(TimeSpan.FromDays(1)).AddHours(18).AddMinutes(45).AddTicks(1).ApplyMoscow().UtcDateTime; } // set ProgressBar bounds _progressBars.ForEach(p => { p.Value = 0; p.Maximum = 100; }); var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // for track logs in output window in Vusial Studio (poor performance). var generateDepths = GenDepthsCheckBox.IsChecked == true; var maxDepth = MaxDepth.Text.To <int>(); var maxVolume = MaxVolume.Text.To <int>(); var secId = security.ToSecurityId(); SetIsEnabled(false, false, false); foreach (var set in settings) { if (set.Item1.IsChecked == false) { continue; } var title = (string)set.Item1.Content; InitChart(set.Item5, set.Item6, set.Item7); var progressBar = set.Item2; var statistic = set.Item3; var emulationInfo = set.Item4; var level1Info = new Level1ChangeMessage { SecurityId = secId, ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secCode == "RIZ2" ? 10m : 0.05m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = Portfolio.CreateSimulator(); var secProvider = (ISecurityProvider) new CollectionSecurityProvider(new[] { security }); // create backtesting connector var connector = new HistoryEmulationConnector( secProvider, new[] { portfolio }) { EmulationAdapter = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } }, //UseExternalCandleSource = emulationInfo.UseCandleTimeFrame != null, //CreateDepthFromOrdersLog = emulationInfo.UseOrderLog, //CreateTradesFromOrdersLog = emulationInfo.UseOrderLog, HistoryMessageAdapter = { StorageRegistry = storageRegistry, OrderLogMarketDepthBuilders = { { secId, new OrderLogMarketDepthBuilder(secId) } } }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; logManager.Sources.Add(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame) { BuildCandlesMode = emulationInfo.UseCandleTimeFrame == null ? MarketDataBuildModes.Build : MarketDataBuildModes.Load, BuildCandlesFrom2 = emulationInfo.UseOrderLog ? DataType.OrderLog : null, }; _shortMa = new SimpleMovingAverage { Length = 10 }; _shortElem = new ChartIndicatorElement { Color = Colors.Coral, ShowAxisMarker = false, FullTitle = _shortMa.ToString() }; var chart = set.Item5; chart.AddElement(_area, _shortElem); _longMa = new SimpleMovingAverage { Length = 80 }; _longElem = new ChartIndicatorElement { ShowAxisMarker = false, FullTitle = _longMa.ToString() }; chart.AddElement(_area, _longElem); // create strategy based on 80 5-min и 10 5-min var strategy = new SmaStrategy(series, _longMa, _shortMa, chart, _candlesElem, _tradesElem, _longElem, _shortElem) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; logManager.Sources.Add(strategy); if (emulationInfo.CustomHistoryAdapter != null) { connector.Adapter.InnerAdapters.Remove(connector.MarketDataAdapter); connector.Adapter.InnerAdapters.Add(new EmulationMessageAdapter(emulationInfo.CustomHistoryAdapter(connector.TransactionIdGenerator), new InMemoryMessageChannel(new MessageByLocalTimeQueue(), "History out", err => err.LogError()), true, connector.EmulationAdapter.Emulator.SecurityProvider, connector.EmulationAdapter.Emulator.PortfolioProvider)); } // set history range connector.HistoryMessageAdapter.StartDate = startTime; connector.HistoryMessageAdapter.StopDate = stopTime; connector.NewSecurity += s => { if (s != security) { return; } // fill level1 values connector.EmulationAdapter.SendInMessage(level1Info); if (emulationInfo.UseMarketDepth) { connector.SubscribeMarketDepth(security); if ( // if order book will be generated generateDepths || // or backtesting will be on candles emulationInfo.UseCandleTimeFrame != TimeSpan.Zero ) { // if no have order book historical data, but strategy is required, // use generator based on last prices connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } if (emulationInfo.UseOrderLog) { connector.SubscribeOrderLog(security); } if (emulationInfo.UseTicks) { connector.SubscribeTrades(security); } if (emulationInfo.UseLevel1) { connector.SubscribeLevel1(security); } // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed connector.Start(); }; // fill parameters panel statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var equity = set.Item6; var pnlCurve = equity.CreateCurve(LocalizedStrings.PnL + " " + emulationInfo.StrategyName, Colors.Green, Colors.Red, ChartIndicatorDrawStyles.Area); var unrealizedPnLCurve = equity.CreateCurve(LocalizedStrings.PnLUnreal + " " + emulationInfo.StrategyName, Colors.Black, ChartIndicatorDrawStyles.Line); var commissionCurve = equity.CreateCurve(LocalizedStrings.Str159 + " " + emulationInfo.StrategyName, Colors.Red, ChartIndicatorDrawStyles.DashedLine); strategy.PnLChanged += () => { var data = new ChartDrawData(); data .Group(strategy.CurrentTime) .Add(pnlCurve, strategy.PnL - strategy.Commission ?? 0) .Add(unrealizedPnLCurve, strategy.PnLManager.UnrealizedPnL ?? 0) .Add(commissionCurve, strategy.Commission ?? 0); equity.Draw(data); }; var posItems = set.Item7.CreateCurve(emulationInfo.StrategyName, emulationInfo.CurveColor, ChartIndicatorDrawStyles.Line); strategy.PositionChanged += () => { var data = new ChartDrawData(); data .Group(strategy.CurrentTime) .Add(posItems, strategy.Position); set.Item7.Draw(data); }; connector.ProgressChanged += steps => this.GuiAsync(() => progressBar.Value = steps); connector.StateChanged += () => { if (connector.State == EmulationStates.Stopped) { strategy.Stop(); SetIsChartEnabled(chart, false); if (_connectors.All(c => c.State == EmulationStates.Stopped)) { logManager.Dispose(); _connectors.Clear(); SetIsEnabled(true, false, false); } this.GuiAsync(() => { if (connector.IsFinished) { progressBar.Value = progressBar.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime), title); } else { MessageBox.Show(this, LocalizedStrings.cancelled, title); } }); } else if (connector.State == EmulationStates.Started) { if (_connectors.All(c => c.State == EmulationStates.Started)) { SetIsEnabled(false, true, true); } SetIsChartEnabled(chart, true); } else if (connector.State == EmulationStates.Suspended) { if (_connectors.All(c => c.State == EmulationStates.Suspended)) { SetIsEnabled(true, false, true); } } }; if (ShowDepth.IsChecked == true) { MarketDepth.UpdateFormat(security); connector.NewMessage += message => { if (message is QuoteChangeMessage quoteMsg) { MarketDepth.UpdateDepth(quoteMsg); } }; } _connectors.Add(connector); progressBar.Value = 0; } _startEmulationTime = DateTime.Now; // start emulation foreach (var connector in _connectors.Cache) { // raise NewSecurities and NewPortfolio for full fill strategy properties connector.Connect(); // 1 cent commission for trade connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); } TabControl.Items.Cast <TabItem>().First(i => i.Visibility == Visibility.Visible).IsSelected = true; }
private void StartBtnClick(object sender, RoutedEventArgs e) { if (HistoryPath.Folder.IsEmpty() || !Directory.Exists(HistoryPath.Folder)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (Math.Abs(TestingProcess.Value - 0) > double.Epsilon) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); // SMA periods var periods = new[] { new Tuple <int, int, Color>(80, 10, Colors.DarkGreen), new Tuple <int, int, Color>(70, 8, Colors.Red), new Tuple <int, int, Color>(60, 6, Colors.DarkBlue) }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Folder) }; var timeFrame = TimeSpan.FromMinutes(5); // create test security var security = new Security { Id = "RIZ2@FORTS", // sec id has the same name as folder with historical data Code = "RIZ2", Name = "RTS-12.12", Board = ExchangeBoard.Forts, }; var startTime = new DateTime(2012, 10, 1); var stopTime = new DateTime(2012, 10, 31); var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector var batchEmulation = new BatchEmulation(new[] { security }, new[] { portfolio }, storageRegistry) { EmulationSettings = { MarketTimeChangedInterval = timeFrame, StartTime = startTime, StopTime = stopTime, // count of parallel testing strategies BatchSize = periods.Length, } }; // handle historical time for update ProgressBar batchEmulation.ProgressChanged += (curr, total) => this.GuiAsync(() => TestingProcess.Value = total); batchEmulation.StateChanged += (oldState, newState) => { if (batchEmulation.State != EmulationStates.Stopped) { return; } this.GuiAsync(() => { if (batchEmulation.IsFinished) { TestingProcess.Value = TestingProcess.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else { MessageBox.Show(this, LocalizedStrings.cancelled); } }); }; // get emulation connector var connector = batchEmulation.EmulationConnector; logManager.Sources.Add(connector); connector.NewSecurities += securities => { if (securities.All(s => s != security)) { return; } // fill level1 values connector.SendInMessage(level1Info); connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { // order book freq refresh is 1 sec Interval = TimeSpan.FromSeconds(1), }); }; TestingProcess.Maximum = 100; TestingProcess.Value = 0; _startEmulationTime = DateTime.Now; var strategies = periods .Select(period => { var candleManager = new CandleManager(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); // create strategy based SMA var strategy = new SmaStrategy(candleManager, series, new SimpleMovingAverage { Length = period.Item1 }, new SimpleMovingAverage { Length = period.Item2 }) { Volume = 1, Security = security, Portfolio = portfolio, Connector = connector, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; strategy.SetCandleManager(candleManager); var curveItems = Curve.CreateCurve(LocalizedStrings.Str3026Params.Put(period.Item1, period.Item2), period.Item3); strategy.PnLChanged += () => { var data = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL, }; this.GuiAsync(() => curveItems.Add(data)); }; Stat.AddStrategies(new[] { strategy }); return(strategy); }) .ToEx(periods.Length); // start emulation batchEmulation.Start(strategies); }
private void StartEmulation() { if (_connector != null && _connector.State != EmulationStates.Stopped) { throw new InvalidOperationException(LocalizedStrings.Str3015); } if (Strategy == null) { throw new InvalidOperationException("Strategy not selected."); } var strategy = (EmulationDiagramStrategy)Strategy; if (strategy.DataPath.IsEmpty() || !Directory.Exists(strategy.DataPath)) { throw new InvalidOperationException(LocalizedStrings.Str3014); } strategy .Composition .Parameters .ForEach(p => { if (p.Type == typeof(Security) && p.Value == null) { throw new InvalidOperationException(LocalizedStrings.Str1380); } }); strategy.Reset(); Reset(); var securityId = "empty@empty"; var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(securityId); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = strategy.CandlesTimeFrame; var useCandles = strategy.MarketDataSource == MarketDataSource.Candles; // create test security var security = new Security { Id = securityId, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(strategy.DataPath) }; var startTime = strategy.StartDate.ChangeKind(DateTimeKind.Utc); var stopTime = strategy.StopDate.ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; var securityProvider = ConfigManager.GetService <ISecurityProvider>(); // create backtesting connector _connector = new HistoryEmulationConnector(securityProvider, new[] { portfolio }, new StorageRegistry()) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = strategy.MatchOnTouch, IsSupportAtomicReRegister = strategy.IsSupportAtomicReRegister, Latency = strategy.EmulatoinLatency, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)_connector).LogLevel = strategy.DebugLog ? LogLevels.Debug : LogLevels.Info; ConfigManager.GetService <LogManager>().Sources.Add(_connector); strategy.Volume = 1; strategy.Portfolio = portfolio; strategy.Security = security; strategy.Connector = _connector; strategy.LogLevel = strategy.DebugLog ? LogLevels.Debug : LogLevels.Info; // by default interval is 1 min, // it is excessively for time range with several months strategy.UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>(); strategy.SetCandleManager(new CandleManager(_connector)); _connector.NewSecurity += s => { //TODO send real level1 message var level1Info = new Level1ChangeMessage { SecurityId = s.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // fill level1 values _connector.SendInMessage(level1Info); if (strategy.UseMarketDepths) { _connector.RegisterMarketDepth(security); if ( // if order book will be generated strategy.GenerateDepths || // of backtesting will be on candles useCandles ) { // if no have order book historical data, but strategy is required, // use generator based on last prices _connector.RegisterMarketDepth(new TrendMarketDepthGenerator(_connector.GetSecurityId(s)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = strategy.MaxDepths, MaxBidsDepth = strategy.MaxDepths, UseTradeVolume = true, MaxVolume = strategy.MaxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) { return; } var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.LookupSecuritiesResult += (ss) => { if (strategy.ProcessState != ProcessStates.Stopped) { return; } // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); this.GuiAsync(() => { if (_connector.IsFinished) { TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; } }); break; case EmulationStates.Started: break; } }; TicksAndDepthsProgress.Value = 0; DiagramDebuggerControl.Debugger.IsEnabled = true; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
protected override TimeSpan OnProcess() { var allSecurity = this.GetAllSecurity(); var source = new GoogleHistorySource(); var startDate = _settings.StartFrom; var endDate = DateTime.Today - TimeSpan.FromDays(_settings.DayOffset); var allDates = startDate.Range(endDate, TimeSpan.FromDays(1)).ToArray(); var hasSecurities = false; foreach (var security in GetWorkingSecurities()) { hasSecurities = true; if (!CanProcess()) { break; } foreach (var pair in (allSecurity ?? security).GetCandleSeries()) { if (!CanProcess()) { break; } if (pair.MessageType != typeof(TimeFrameCandleMessage)) { this.AddWarningLog(LocalizedStrings.Str2296Params, pair); continue; } var tf = (TimeSpan)pair.Arg; var storage = StorageRegistry.GetCandleMessageStorage(pair.MessageType, security.Security, tf, _settings.Drive, _settings.StorageFormat); var emptyDates = allDates.Except(storage.Dates).ToArray(); if (emptyDates.IsEmpty()) { this.AddInfoLog(LocalizedStrings.Str2297Params, tf, security.Security.Id); continue; } var currDate = emptyDates.First(); var lastDate = emptyDates.Last(); while (currDate <= lastDate) { if (!CanProcess()) { break; } if (_settings.IgnoreWeekends && !security.IsTradeDate(currDate)) { this.AddDebugLog(LocalizedStrings.WeekEndDate, currDate); currDate = currDate.AddDays(1); continue; } try { var till = currDate + TimeSpan.FromDays(_settings.CandleDayStep).Max(tf); this.AddInfoLog(LocalizedStrings.Str2298Params, tf, currDate, till, security.Security.Id); var candles = source.GetCandles(security.Security, tf, currDate, till); if (candles.Any()) { SaveCandles(security, candles); } else { this.AddDebugLog(LocalizedStrings.NoData); } } catch (Exception ex) { HandleError(new InvalidOperationException(LocalizedStrings.Str2299Params .Put(tf, currDate, security.Security.Id), ex)); } currDate = currDate.AddDays(_settings.CandleDayStep); } } } if (!hasSecurities) { this.AddWarningLog(LocalizedStrings.Str2292); return(TimeSpan.MaxValue); } if (CanProcess()) { this.AddInfoLog(LocalizedStrings.Str2300); } return(base.OnProcess()); }
private void LoadData(CandleSeries series) { var msgType = series.CandleType.ToCandleMessageType(); _transactionId = _transactionIdGenerator.GetNextId(); _holder.Clear(); _holder.CreateCandleSeries(_transactionId, series); _candleTransform.Process(new ResetMessage()); _candleBuilder = _builderProvider.Get(msgType); var storage = new StorageRegistry(); //BusyIndicator.IsBusy = true; var path = HistoryPath.Folder; var isBuild = BuildFromTicks.IsChecked == true; var format = Format.SelectedFormat; var maxDays = (isBuild || series.CandleType != typeof(TimeFrameCandle)) ? 2 : 30 * (int)((TimeSpan)series.Arg).TotalMinutes; _mdMsg = series.ToMarketDataMessage(true); Task.Factory.StartNew(() => { var date = DateTime.MinValue; if (isBuild) { foreach (var tick in storage.GetTickMessageStorage(series.Security.ToSecurityId(), new LocalMarketDataDrive(path), format).Load()) { _tradeGenerator.Process(tick); if (_candleTransform.Process(tick)) { var candles = _candleBuilder.Process(_mdMsg, _currCandle, _candleTransform); foreach (var candle in candles) { _currCandle = candle; _updatedCandles.Add((CandleMessage)candle.Clone()); } } _lastTime = tick.ServerTime; if (date != tick.ServerTime.Date) { date = tick.ServerTime.Date; //var str = date.To<string>(); //this.GuiAsync(() => BusyIndicator.BusyContent = str); maxDays--; if (maxDays == 0) { break; } } } } else { foreach (var candleMsg in storage.GetCandleMessageStorage(msgType, series.Security.ToSecurityId(), series.Arg, new LocalMarketDataDrive(path), format).Load()) { if (candleMsg.State != CandleStates.Finished) { candleMsg.State = CandleStates.Finished; } _currCandle = candleMsg; _updatedCandles.Add(candleMsg); _lastTime = candleMsg.OpenTime; if (candleMsg is TimeFrameCandleMessage) { _lastTime += (TimeSpan)series.Arg; } _tradeGenerator.Process(new ExecutionMessage { ExecutionType = ExecutionTypes.Tick, SecurityId = series.Security.ToSecurityId(), ServerTime = _lastTime, TradePrice = candleMsg.ClosePrice, }); if (date != candleMsg.OpenTime.Date) { date = candleMsg.OpenTime.Date; //var str = date.To<string>(); //this.GuiAsync(() => BusyIndicator.BusyContent = str); maxDays--; if (maxDays == 0) { break; } } } } _historyLoaded = true; }) .ContinueWith(t => { if (t.Exception != null) { Error(t.Exception.Message); } //BusyIndicator.IsBusy = false; Chart.IsAutoRange = false; ModifyAnnotationBtn.IsEnabled = true; NewAnnotationBtn.IsEnabled = true; }, TaskScheduler.FromCurrentSynchronizationContext()); }
private void StartBtnClick(object sender, RoutedEventArgs e) { if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (Math.Abs(TestingProcess.Value - 0) > double.Epsilon) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); // SMA periods var periods = new[] { new Tuple<int, int, Color>(80, 10, Colors.DarkGreen), new Tuple<int, int, Color>(70, 8, Colors.Red), new Tuple<int, int, Color>(60, 6, Colors.DarkBlue) }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Text) }; var timeFrame = TimeSpan.FromMinutes(5); // create test security var security = new Security { Id = "RIZ2@FORTS", // sec id has the same name as folder with historical data Code = "RIZ2", Name = "RTS-12.12", Board = ExchangeBoard.Forts, }; var startTime = new DateTime(2012, 10, 1); var stopTime = new DateTime(2012, 10, 31); var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector var batchEmulation = new BatchEmulation(new[] { security }, new[] { portfolio }, storageRegistry) { EmulationSettings = { MarketTimeChangedInterval = timeFrame, StartTime = startTime, StopTime = stopTime, // count of parallel testing strategies BatchSize = periods.Length, } }; // handle historical time for update ProgressBar batchEmulation.ProgressChanged += (curr, total) => this.GuiAsync(() => TestingProcess.Value = total); batchEmulation.StateChanged += (oldState, newState) => { if (batchEmulation.State != EmulationStates.Stopped) return; this.GuiAsync(() => { if (batchEmulation.IsFinished) { TestingProcess.Value = TestingProcess.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else MessageBox.Show(this, LocalizedStrings.cancelled); }); }; // получаем подключение для эмуляции var connector = batchEmulation.EmulationConnector; logManager.Sources.Add(connector); connector.NewSecurities += securities => { if (securities.All(s => s != security)) return; // fill level1 values connector.SendInMessage(level1Info); connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { // order book freq refresh is 1 sec Interval = TimeSpan.FromSeconds(1), }); }; TestingProcess.Maximum = 100; TestingProcess.Value = 0; _startEmulationTime = DateTime.Now; var strategies = periods .Select(period => { var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); // create strategy based SMA var strategy = new SmaStrategy(series, new SimpleMovingAverage { Length = period.Item1 }, new SimpleMovingAverage { Length = period.Item2 }) { Volume = 1, Security = security, Portfolio = portfolio, Connector = connector, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>() }; strategy.SetCandleManager(new CandleManager(connector)); var curveItems = Curve.CreateCurve(LocalizedStrings.Str3026Params.Put(period.Item1, period.Item2), period.Item3); strategy.PnLChanged += () => { var data = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL, }; this.GuiAsync(() => curveItems.Add(data)); }; Stat.AddStrategies(new[] { strategy }); return strategy; }) .ToEx(periods.Length); // start emulation batchEmulation.Start(strategies); }
private void OnOptimizeClick(object sender, RoutedEventArgs e) { btnOptimize.IsEnabled = false; _log.AddLog(new LogMessage(_log, DateTime.Now, LogLevels.Info, "Optimization is beginning..")); this.InitGrids(); // создаем тестовый инструмент, на котором будет производится тестирование var security = new Security { Id = this.txtSecurityId.Text, // по идентификатору инструмента будет искаться папка с историческими маркет данными Code = this.txtSecurityCode.Text, Name = this.txtSecurityCode.Text, MinStepSize = 1, MinStepPrice = 1, ExchangeBoard = ExchangeBoard.Forts, }; var storageRegistry = new StorageRegistry(); ((LocalMarketDataDrive) storageRegistry.DefaultDrive).Path = this.txtHistoryPath.Text; ((LocalMarketDataDrive) storageRegistry.DefaultDrive).UseAlphabeticPath = true; var portfolio = new Portfolio { Name = "test account", BeginValue = 30000m }; DateTime startTime; DateTime stopTime; if (!DateTime.TryParse(txtHistoryRangeEnd.Text, out stopTime)) { stopTime = DateTime.Now; txtHistoryRangeEnd.Text = stopTime.ToString(); } if (!DateTime.TryParse(txtHistoryRangeBegin.Text, out startTime)) { startTime = stopTime.AddDays(-3); txtHistoryRangeBegin.Text = startTime.ToString(); } Stopwatch sw = new Stopwatch(); EMAStrategyOptimizer optimizer = new EMAStrategyOptimizer(security, storageRegistry, portfolio, startTime, stopTime) { Volume = this.Volume, Log = _log }; optimizer.StateChanged += () => { if (optimizer.State == OptimizationState.Finished) { sw.Stop(); _log.AddLog(new LogMessage(_log, DateTime.Now, LogLevels.Info, String.Format("Opt done ({0}). The best startegy: PnL: {1}, {2}", sw.Elapsed, optimizer.BestResult.Value.PnLManager.PnL, optimizer.BestResult.Key ))); optimizer.BestResult.Value.Trader.Orders.ForEach(OnOrderRegistered); OnNewTrades(optimizer.BestResult.Value.Trader.MyTrades); this.GuiAsync(() => { this.UpdateStrategyStat(optimizer.BestResult.Value); this.InitChart(optimizer.BestResult.Value); optimizer.BestResult.Value.CandleSeries.GetCandles<TimeFrameCandle>().ForEach(DrawCandleAndEma); optimizer.BestResult.Value.Trader.MyTrades.ForEach(DrawTrade); // Replace MainOptVarItem with optimized one this.MainOptVarItem = optimizer.BestResult.Key; btnOptimize.IsEnabled = true; }); } }; sw.Start(); optimizer.Optimize(); }
private void OnStartClick(object sender, RoutedEventArgs e) { if (_strategy != null && _strategy.ProcessState != ProcessStates.Stopped) { _strategy.Stop(); return; } if (this.Portfolios.SelectedPortfolio == null) { this.Portfolios.SelectedIndex = this.Portfolios.Items.Count - 1; } this.InitGrids(); _candleManager = new CandleManager(_trader); // Добавление в источник свечек TimeFrameCandleBuilder источник данных в виде файлов гидры var storageRegistry = new StorageRegistry(); ((LocalMarketDataDrive) storageRegistry.DefaultDrive).Path = this.txtHistoryPath.Text; ((LocalMarketDataDrive) storageRegistry.DefaultDrive).UseAlphabeticPath = true; var cbs = new TradeStorageCandleBuilderSource { StorageRegistry = storageRegistry }; _candleManager.Sources.OfType<TimeFrameCandleBuilder>().Single().Sources.Add(cbs); // регистрируем наш тайм-фрейм var series = new CandleSeries(typeof(TimeFrameCandle), _security, this.MainOptVarItem.TimeFrame); _strategy = new EMAEventModelStrategy(series, new ExponentialMovingAverage { Length = this.MainOptVarItem.FilterOptPeriod}, new ExponentialMovingAverage { Length = this.MainOptVarItem.LongOptPeriods }, new ExponentialMovingAverage { Length = this.MainOptVarItem.ShortOptPeriods }, this.MainOptVarItem.TakeProfitUnit, this.MainOptVarItem.StopLossUnit) { Volume = this.Volume, Security = _security, Portfolio = this.Portfolios.SelectedPortfolio, Trader = _trader, UseQuoting = true //_trader is QuikTrader // hack to turn quoting off for RealTimeEmulationTrader }; DateTime startTime; if (!DateTime.TryParse(txtHistoryRangeBegin.Text, out startTime)) { startTime = DateTime.Now.AddDays(-3); txtHistoryRangeBegin.Text = startTime.ToString(); } this.InitChart(_strategy); _candleManager.Processing += (candleSeries, candle) => { if (candle.State == CandleStates.Finished) { this.GuiAsync(() => DrawCandleAndEma(candle)); } }; _candleManager.Start(series, startTime, DateTime.MaxValue); // Subscribe UI to all strategy actions _strategy.Trader.NewOrders += orders => orders.ForEach(OnOrderRegistered); _strategy.Trader.NewMyTrades += OnNewTrades; _strategy.Trader.NewMyTrades += trades => this.GuiAsync(() => trades.ForEach(DrawTrade)); _strategy.PropertyChanged += (o, args) => this.GuiAsync(() => OnStrategyPropertyChanged(o, args)); _strategy.ProcessStateChanged += strategy => { if (strategy.ProcessState == ProcessStates.Started) { this.Start.Content = "Stop"; } else if (strategy.ProcessState == ProcessStates.Stopped) { this.Start.Content = "Start"; } }; _logManager.Sources.Add(_strategy); // запускаем процесс получения стакана, необходимый для работы алгоритма котирования _strategy.Start(); this.Start.Content = "Stop"; }
private void LoadData(string path) { var storage = new StorageRegistry(); var maxDays = 2; BusyIndicator.IsBusy = true; Task.Factory.StartNew(() => { var date = DateTime.MinValue; foreach (var tick in storage.GetTickMessageStorage(_security, new LocalMarketDataDrive(path)).Load()) { AppendTick(tick.ServerTime, tick.TradePrice.Value, tick.Volume.Value); _lastTime = tick.ServerTime; if (date != tick.ServerTime.Date) { date = tick.ServerTime.Date; this.GuiAsync(() => { BusyIndicator.BusyContent = date.ToString(); }); maxDays--; if (maxDays == 0) break; } } }) .ContinueWith(t => { if (t.Exception != null) Error(t.Exception.Message); BusyIndicator.IsBusy = false; }, TaskScheduler.FromCurrentSynchronizationContext()); }
private void StartButtonOnClick(object sender, RoutedEventArgs e) { _logManager.Sources.Clear(); _bufferedChart.ClearAreas(); Curve.Clear(); PositionCurve.Clear(); if (HistoryPathTextBox.Text.IsEmpty() || !Directory.Exists(HistoryPathTextBox.Text)) { MessageBox.Show("Wrong path."); return; } if (_connector != null && _connector.State != EmulationStates.Stopped) { MessageBox.Show("Already launched."); return; } if (Composition == null) { MessageBox.Show("No strategy selected."); return; } var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(SecusityTextBox.Text); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = (TimeSpan)TimeFrameComboBox.SelectedItem; var useCandles = (string)MarketDataTypeComboBox.SelectedItem != "Ticks"; // create test security var security = new Security { Id = SecusityTextBox.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPathTextBox.Text) }; var startTime = ((DateTime)FromDatePicker.Value).ChangeKind(DateTimeKind.Utc); var stopTime = ((DateTime)ToDatePicke.Value).ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector _connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; //((ILogSource)_connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; _logManager.Sources.Add(_connector); var candleManager = !useCandles ? new CandleManager(new TradeCandleBuilderSourceEx(_connector)) : new CandleManager(_connector); // create strategy based on 80 5-min и 10 5-min var strategy = new DiagramStrategy { Volume = 1, Portfolio = portfolio, Security = security, Connector = _connector, //LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, Composition = Composition, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>() }; strategy.SetChart(_bufferedChart); strategy.SetCandleManager(candleManager); _logManager.Sources.Add(strategy); strategy.OrderRegistering += OnStrategyOrderRegistering; strategy.OrderReRegistering += OnStrategyOrderReRegistering; strategy.OrderRegisterFailed += OnStrategyOrderRegisterFailed; strategy.StopOrderRegistering += OnStrategyOrderRegistering; strategy.StopOrderReRegistering += OnStrategyOrderReRegistering; strategy.StopOrderRegisterFailed += OnStrategyOrderRegisterFailed; strategy.NewMyTrades += OnStrategyNewMyTrade; var pnlCurve = Curve.CreateCurve(LocalizedStrings.PnL + " " + strategy.Name, Colors.DarkGreen, EquityCurveChartStyles.Area); var unrealizedPnLCurve = Curve.CreateCurve(LocalizedStrings.PnLUnreal + strategy.Name, Colors.Black); var commissionCurve = Curve.CreateCurve(LocalizedStrings.Str159 + " " + strategy.Name, Colors.Red, EquityCurveChartStyles.DashedLine); strategy.PnLChanged += () => { var pnl = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL }; var commission = new EquityData { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; var posItems = PositionCurve.CreateCurve(strategy.Name, Colors.DarkGreen); strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); _connector.NewSecurities += securities => { if (securities.All(s => s != security)) return; // fill level1 values _connector.SendInMessage(level1Info); //_connector.RegisterMarketDepth(security); if (!useCandles) _connector.RegisterTrades(security); // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) return; var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To<TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); SetIsEnabled(false); this.GuiAsync(() => { if (_connector.IsFinished) { TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; MessageBox.Show("Done."); } else MessageBox.Show("Cancelled."); }); break; case EmulationStates.Started: SetIsEnabled(true); break; } }; TicksAndDepthsProgress.Value = 0; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
private void Convert_OnClick(object sender, RoutedEventArgs e) { Convert.IsEnabled = false; if (_isStarted) { _logManager.Application.AddInfoLog("Остановка конвертации."); _isStarted = false; return; } QshFolder.IsEnabled = StockSharpFolder.IsEnabled = Format.IsEnabled = false; _logManager.Application.AddInfoLog("Запуск конвертации."); _isStarted = true; var settings = new Settings { QshFolder = QshFolder.Folder, StockSharpFolder = StockSharpFolder.Folder, Format = Format.GetSelectedValue<StorageFormats>() ?? StorageFormats.Binary }; try { new XmlSerializer<Settings>().Serialize(settings, _settingsFile); } catch (Exception ex) { ex.LogError(); } Task.Factory.StartNew(() => { var registry = new StorageRegistry(); ((LocalMarketDataDrive)registry.DefaultDrive).Path = settings.StockSharpFolder; this.GuiAsync(() => { Convert.Content = "Остановить"; Convert.IsEnabled = true; }); ConvertDirectory(settings.QshFolder, registry, settings.Format, ExchangeBoard.Forts /* TODO надо сделать выбор в GUI */); }) .ContinueWith(t => { Convert.Content = "Запустить"; Convert.IsEnabled = true; QshFolder.IsEnabled = StockSharpFolder.IsEnabled = Format.IsEnabled = true; if (t.IsFaulted) { t.Exception.LogError(); new MessageBoxBuilder() .Text("В процессе конвертации произошла ошибка. Ошибка записана в лог.") .Error() .Owner(this) .Show(); return; } new MessageBoxBuilder() .Text("Конвертация {0}.".Put(_isStarted ? "выполнена" : "остановлена")) .Owner(this) .Show(); }, TaskScheduler.FromCurrentSynchronizationContext()); }
private void LoadData() { _candle = null; _lastPrice = 0m; _allCandles.Clear(); var id = new SecurityIdGenerator().Split(SecurityId.Text); _security = new Security { Id = SecurityId.Text, PriceStep = 5, Board = ExchangeBoard.GetBoard(id.BoardCode) }; Chart.Reset(new IChartElement[] { _candleElement1 }); var storage = new StorageRegistry(); var maxDays = 2; BusyIndicator.IsBusy = true; var path = HistoryPath.Folder; Task.Factory.StartNew(() => { var date = DateTime.MinValue; foreach (var tick in storage.GetTickMessageStorage(_security, new LocalMarketDataDrive(path)).Load()) { AppendTick(_security, tick); _lastTime = tick.ServerTime; if (date != tick.ServerTime.Date) { date = tick.ServerTime.Date; this.GuiAsync(() => { BusyIndicator.BusyContent = date.ToString(); }); maxDays--; if (maxDays == 0) break; } } }) .ContinueWith(t => { if (t.Exception != null) Error(t.Exception.Message); BusyIndicator.IsBusy = false; }, TaskScheduler.FromCurrentSynchronizationContext()); }
/// <summary> /// The method is called when the <see cref="Strategy.Start"/> method has been called and the <see cref="Strategy.ProcessState"/> state has been taken the <see cref="ProcessStates.Started"/> value. /// </summary> protected override void OnStarted() { var storateRegistry = new StorageRegistry(); var drive = Environment.GetValue<IMarketDataDrive>("Drive"); if (drive != null) storateRegistry.DefaultDrive = drive; StorateRegistry = storateRegistry; ThreadingHelper .Thread(() => { try { OnAnalyze(); } catch (Exception ex) { this.AddErrorLog(ex); ex.LogError(); } }) .Name("{0} analyze thread.".Put(Name)) .Culture(CultureInfo.InvariantCulture) .Launch(); }
protected override TimeSpan OnProcess() { var source = new TrueFXSource(); if (_settings.UseTemporaryFiles != TempFiles.NotUse) { source.DumpFolder = GetTempPath(); } var allSecurity = this.GetAllSecurity(); // если фильтр по инструментам выключен (выбран инструмент все инструменты) IEnumerable <HydraTaskSecurity> selectedSecurities = (allSecurity != null ? this.ToHydraSecurities(EntityRegistry.Securities.Filter(ExchangeBoard.TrueFX)) : Settings.Securities ).ToArray(); if (selectedSecurities.IsEmpty()) { this.AddWarningLog(LocalizedStrings.Str2289); source.Refresh(EntityRegistry.Securities, new Security(), SaveSecurity, () => !CanProcess(false)); selectedSecurities = this.ToHydraSecurities(EntityRegistry.Securities.Filter(ExchangeBoard.TrueFX)); } if (selectedSecurities.IsEmpty()) { this.AddWarningLog(LocalizedStrings.Str2292); return(TimeSpan.MaxValue); } var startDate = _settings.StartFrom; var endDate = DateTime.Today - TimeSpan.FromDays(_settings.DayOffset); var allDates = startDate.Range(endDate, TimeSpan.FromDays(1)).ToArray(); foreach (var security in selectedSecurities) { if (!CanProcess()) { break; } if ((allSecurity ?? security).IsLevel1Enabled()) { var storage = StorageRegistry.GetLevel1MessageStorage(security.Security, _settings.Drive, _settings.StorageFormat); var emptyDates = allDates.Except(storage.Dates).ToArray(); if (emptyDates.IsEmpty()) { this.AddInfoLog(LocalizedStrings.Str2293Params, security.Security.Id); } else { var secId = security.Security.ToSecurityId(); foreach (var emptyDate in emptyDates) { if (!CanProcess()) { break; } try { this.AddInfoLog(LocalizedStrings.Str2294Params, emptyDate, security.Security.Id); var ticks = source.LoadTickMessages(secId, emptyDate); if (ticks.Any()) { SaveLevel1Changes(security, ticks); } else { this.AddDebugLog(LocalizedStrings.NoData); } if (_settings.UseTemporaryFiles == TempFiles.UseAndDelete) { File.Delete(source.GetDumpFile(security.Security, emptyDate, emptyDate, typeof(Level1ChangeMessage), null)); } } catch (Exception ex) { HandleError(new InvalidOperationException(LocalizedStrings.Str2295Params .Put(emptyDate, security.Security.Id), ex)); } } } } else { this.AddDebugLog(LocalizedStrings.MarketDataNotEnabled, security.Security.Id, typeof(Level1ChangeMessage).Name); } if (!CanProcess()) { break; } } if (CanProcess()) { this.AddInfoLog(LocalizedStrings.Str2300); } return(base.OnProcess()); }