public PortfolioCommand(Portfolio portfolio, bool isNew) { if (portfolio == null) throw new ArgumentNullException("portfolio"); Portfolio = portfolio; IsNew = isNew; }
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() { try { Console.Write(LocalizedStrings.Str2992); var account1 = Console.ReadLine(); Console.Write(LocalizedStrings.Str2993); var account2 = Console.ReadLine(); using (var quikTrader1 = new QuikTrader { LuaFixServerAddress = "127.0.0.1:5001".To<EndPoint>() }) using (var quikTrader2 = new QuikTrader { LuaFixServerAddress = "127.0.0.1:5002".To<EndPoint>() }) { // подписываемся на событие ошибок обработки данных и разрыва соединения // quikTrader1.Error += OnError; quikTrader2.Error += OnError; quikTrader1.ConnectionError += OnError; quikTrader2.ConnectionError += OnError; var portfoliosWait = new ManualResetEvent(false); Action<IEnumerable<Portfolio>> newPortfolios = portfolios => { if (_portfolio1 == null) _portfolio1 = portfolios.FirstOrDefault(p => p.Name == account1); if (_portfolio2 == null) _portfolio2 = portfolios.FirstOrDefault(p => p.Name == account2); // если оба инструмента появились if (_portfolio1 != null && _portfolio2 != null) portfoliosWait.Set(); }; // подписываемся на события новых портфелей quikTrader1.NewPortfolios += newPortfolios; quikTrader2.NewPortfolios += newPortfolios; var securitiesWait = new ManualResetEvent(false); // подписываемся на события новых инструментов quikTrader1.NewSecurities += securities => { if (_lkoh == null) _lkoh = securities.FirstOrDefault(s => s.Code == "LKOH"); // если оба инструмента появились if (_lkoh != null && _riz0 != null) securitiesWait.Set(); }; quikTrader2.NewSecurities += securities => { if (_riz0 == null) _riz0 = securities.FirstOrDefault(s => s.Code == "RIZ0"); // если оба инструмента появились if (_lkoh != null && _riz0 != null) securitiesWait.Set(); }; // запускаем экспорты в Quik-ах, когда получим событие об успешном соединении // quikTrader1.Connected += () => { Console.WriteLine(LocalizedStrings.Str2994Params.Put(quikTrader1.LuaFixServerAddress)); }; quikTrader2.Connected += () => { Console.WriteLine(LocalizedStrings.Str2994Params.Put(quikTrader2.LuaFixServerAddress)); }; // производим подключение каждого из QuikTrader-а // quikTrader1.Connect(); quikTrader2.Connect(); Console.WriteLine(LocalizedStrings.Str2995); portfoliosWait.WaitOne(); securitiesWait.WaitOne(); Console.WriteLine(LocalizedStrings.Str2996); if (_lkoh.BestBid == null || _riz0.BestBid == null) throw new Exception(LocalizedStrings.Str2990); quikTrader1.RegisterOrder(new Order { Portfolio = _portfolio1, Volume = 1, Security = _lkoh, Price = _lkoh.BestBid.Price }); Console.WriteLine(LocalizedStrings.Str2997); quikTrader2.RegisterOrder(new Order { Portfolio = _portfolio2, Volume = 1, Security = _riz0, Price = _riz0.BestBid.Price }); Console.WriteLine(LocalizedStrings.Str2998); } } catch (Exception ex) { Console.WriteLine(ex); } }
public override void LookupPortfolios(Portfolio criteria) { _realConnector.LookupPortfolios(criteria); }
private void SendPortfolio(Portfolio portfolio) { SendInMessage(portfolio.ToMessage()); var money = _initialMoney[portfolio]; SendInMessage( EmulationAdapter .CreatePortfolioChangeMessage(portfolio.Name) .Add(PositionChangeTypes.BeginValue, money) .Add(PositionChangeTypes.CurrentValue, money) .Add(PositionChangeTypes.BlockedValue, 0m)); }
/// <summary> /// Создать копию объекта <see cref="Portfolio"/>. /// </summary> /// <returns>Копия объекта.</returns> public Portfolio Clone() { var clone = new Portfolio(); CopyTo(clone); return clone; }
/// <summary> /// Отменить группу заявок на бирже по фильтру. /// </summary> /// <param name="transactionId">Идентификатор транзакции отмены.</param> /// <param name="isStopOrder"><see langword="true"/>, если нужно отменить только стоп-заявки, false - если только обычный и null - если оба типа.</param> /// <param name="portfolio">Портфель. Если значение равно null, то портфель не попадает в фильтр снятия заявок.</param> /// <param name="direction">Направление заявки. Если значение равно null, то направление не попадает в фильтр снятия заявок.</param> /// <param name="board">Торговая площадка. Если значение равно null, то площадка не попадает в фильтр снятия заявок.</param> /// <param name="security">Инструмент. Если значение равно null, то инструмент не попадает в фильтр снятия заявок.</param> protected override void OnCancelOrders(long transactionId, bool? isStopOrder = null, Portfolio portfolio = null, Sides? direction = null, ExchangeBoard board = null, Security security = null) { if (Version == SmartComVersions.V2 && isStopOrder == null && portfolio == null && direction == null && board == null && security == null) base.OnCancelOrders(transactionId); else this.CancelOrders(Orders, isStopOrder, portfolio, direction, board, security); }
private void Button_Click(object sender, RoutedEventArgs e) { var wnd = new PortfolioPickerWindow(); if (Portfolios != null) wnd.Portfolios = Portfolios; if (wnd.ShowModal(this)) { SelectedPortfolio = wnd.SelectedPortfolio; } }
/// <summary> /// Отменить группу заявок на бирже по фильтру. /// </summary> /// <param name="transactionId">Идентификатор транзакции отмены.</param> /// <param name="isStopOrder"><see langword="true"/>, если нужно отменить только стоп-заявки, <see langword="false"/> - если только обычный и <see langword="null"/> - если оба типа.</param> /// <param name="portfolio">Портфель. Если значение равно <see langword="null"/>, то портфель не попадает в фильтр снятия заявок.</param> /// <param name="direction">Направление заявки. Если значение равно <see langword="null"/>, то направление не попадает в фильтр снятия заявок.</param> /// <param name="board">Торговая площадка. Если значение равно <see langword="null"/>, то площадка не попадает в фильтр снятия заявок.</param> /// <param name="security">Инструмент. Если значение равно <see langword="null"/>, то инструмент не попадает в фильтр снятия заявок.</param> protected override void OnCancelOrders(long transactionId, bool? isStopOrder = null, Portfolio portfolio = null, Sides? direction = null, ExchangeBoard board = null, Security security = null) { if (security != null && portfolio != null && security.Type == SecurityTypes.Future && !security.UnderlyingSecurityId.IsEmpty()) base.OnCancelOrders(transactionId, isStopOrder, portfolio, direction, board, security); else this.CancelOrders(Orders, isStopOrder, portfolio, direction, board); }
private void ChangePosition(Security security, Portfolio portfolio, decimal diff) { if (security == null) throw new ArgumentNullException("security"); if (portfolio == null) throw new ArgumentNullException("portfolio"); bool isNew; var position = _positions.SafeAdd(Tuple.Create(security, portfolio), key => new Position { Security = key.Item1, Portfolio = key.Item2 }, out isNew); position.CurrentValue += diff; if (isNew) NewPosition.SafeInvoke(position); else PositionChanged.SafeInvoke(position); }
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; var settings = strategy.EmulationSettings; if (settings.MarketDataSettings == null) throw new InvalidOperationException(LocalizedStrings.Str3014); new SetDefaultEmulationSettingsCommand(settings).Process(this); 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 = settings.CandlesTimeFrame; var useCandles = settings.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 StudioStorageRegistry { MarketDataSettings = settings.MarketDataSettings }; var startTime = settings.StartDate.ChangeKind(DateTimeKind.Utc); var stopTime = settings.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 = settings.MatchOnTouch, IsSupportAtomicReRegister = settings.IsSupportAtomicReRegister, Latency = settings.EmulatoinLatency, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, StorageFormat = settings.StorageFormat, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)_connector).LogLevel = settings.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 = settings.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 (settings.UseMarketDepths) { _connector.RegisterMarketDepth(security); if ( // if order book will be generated settings.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 = settings.MaxDepths, MaxBidsDepth = settings.MaxDepths, UseTradeVolume = true, MaxVolume = settings.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; } }; _connector.Disconnected += () => { this.GuiAsync(() => _connector.Dispose()); }; 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) { 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); }
public void Connect() { trader = new IQFeedTrader(); trader.Connected += () => SetConnectionStatus(0); trader.Disconnected += () => SetConnectionStatus(1); trader.ConnectionError += error => SetConnectionStatus(error); trader.Connect(); var monitor = new MonitorWindow(); monitor.Show(); logManager.Listeners.Add(new GuiLogListener(monitor)); logManager.Sources.Add(trader); var address = IPAddress.Parse("72.5.42.156"); Trader = new BlackwoodTrader(); logManager.Sources.Add(Trader); Trader.Login = "******"; Trader.Password = "******"; Trader.ExecutionAddress = new IPEndPoint(address, BlackwoodAddresses.ExecutionPort); Trader.MarketDataAddress = new IPEndPoint(address, BlackwoodAddresses.MarketDataPort); Trader.HistoricalDataAddress = new IPEndPoint(address, BlackwoodAddresses.HistoricalDataPort); Trader.Connected += Trader_Connected; Trader.ConnectionError += Trader_ConnectionError; Trader.Disconnected += Trader_Disconnected; Trader.NewPortfolios += portfolios => { foreach (var portfolio in portfolios) { Portfolio = portfolio; Debug.Print("Portfolio name {0}", portfolio.Name); Debug.Print("Portfolio RealizedPnL {0}", portfolio.RealizedPnL); Debug.Print("Portfolio UnrealizedPnL {0}", portfolio.UnrealizedPnL); } }; Trader.NewCandles += Trader_NewCandles; Trader.NewOrders += Trader_NewOrders; Trader.NewPositions += Trader_NewPositions; Trader.Connect(); }
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; }
/// <summary> /// Create position. /// </summary> /// <param name="portfolio">Portfolio.</param> /// <param name="security">Security.</param> /// <returns>Created position.</returns> public override Position CreatePosition(Portfolio portfolio, Security security) { return _entityRegistry.Positions.ReadBySecurityAndPortfolio(security, portfolio) ?? base.CreatePosition(portfolio, security); }
/// <summary> /// The selected item change event handler. /// </summary> /// <param name="e">The event parameter.</param> protected override void OnSelectionChanged(SelectionChangedEventArgs e) { SelectedPortfolio = (Portfolio)SelectedItem; base.OnSelectionChanged(e); }
private void StartBtnClick(object sender, RoutedEventArgs e) { // if process was already started, will stop it now if (_connector != null && _connector.State != EmulationStates.Stopped) { _strategy.Stop(); _connector.Disconnect(); _logManager.Sources.Clear(); _connector = null; return; } // create test security var security = new Security { Id = "AAPL@NASDAQ", Code = "AAPL", Name = "AAPL Inc", Board = ExchangeBoard.Nasdaq, }; var startTime = new DateTime(2009, 6, 1); var stopTime = new DateTime(2009, 9, 1); 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, }; var timeFrame = TimeSpan.FromMinutes(5); // create backtesting connector _connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { HistoryMessageAdapter = { // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; _logManager.Sources.Add(_connector); var candleManager = new CandleManager(_connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); // create strategy based on 80 5-min и 10 5-min _strategy = new SmaStrategy(series, new SimpleMovingAverage { Length = 80 }, new SimpleMovingAverage { Length = 10 }) { Volume = 1, Security = security, Portfolio = portfolio, Connector = _connector, }; _connector.NewSecurities += securities => { if (securities.All(s => s != security)) return; // fill level1 values _connector.SendInMessage(level1Info); _connector.RegisterTrades(new RandomWalkTradeGenerator(_connector.GetSecurityId(security))); _connector.RegisterMarketDepth(new TrendMarketDepthGenerator(_connector.GetSecurityId(security)) { GenerateDepthOnEachTrade = false }); // 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 ParameterGrid.Parameters.Clear(); ParameterGrid.Parameters.AddRange(_strategy.StatisticManager.Parameters); _strategy.PnLChanged += () => { var data = new EquityData { Time = _strategy.CurrentTime, Value = _strategy.PnL, }; this.GuiAsync(() => _curveItems.Add(data)); }; _logManager.Sources.Add(_strategy); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); var nextTime = startTime + progressStep; TestingProcess.Maximum = 100; TestingProcess.Value = 0; // handle historical time for update ProgressBar _connector.MarketTimeChanged += diff => { 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(() => TestingProcess.Value = steps); }; _connector.StateChanged += () => { if (_connector.State == EmulationStates.Stopped) { this.GuiAsync(() => { Report.IsEnabled = true; if (_connector.IsFinished) { TestingProcess.Value = TestingProcess.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else MessageBox.Show(this, LocalizedStrings.cancelled); }); } }; _curveItems.Clear(); Report.IsEnabled = false; _startEmulationTime = DateTime.Now; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); }
static void Main() { try { // для теста выбираем бумагу Лукойл const string secCode = "LKOH"; var quikPath = QuikTerminal.GetDefaultPath(); if (quikPath.IsEmpty()) { Console.WriteLine("Не найден ни один запущенный Quik"); return; } Console.WriteLine("Запущенный Quik найден по пути " + quikPath); Console.Write("Введите код клиента, через который будет выставлена заявка: "); var account = Console.ReadLine(); using (var waitHandle = new AutoResetEvent(false)) { // создаем шлюз к Quik-у using (var trader = new QuikTrader(quikPath)) { // необходимо раскомментировать, если идет работа с РТС Стандарт //trader.FormatTransaction += builder => builder.RemoveInstruction(TransactionBuilder.ExecutionCondition); // подписываемся на событие успешного подключения // все действия необходимо производить только после подключения trader.Connected += () => { Console.WriteLine("Подключение было произведено успешно."); // извещаем об успешном соединени waitHandle.Set(); }; Console.WriteLine("Производим подключение..."); trader.Connect(); // дожидаемся события об успешном соединении waitHandle.WaitOne(); trader.NewPortfolios += portfolios => { if (_portfolio == null) { // находим Лукойл и присваиваем ее переменной lkoh _portfolio = portfolios.FirstOrDefault(p => p.Name == account); if (_portfolio != null) { Console.WriteLine("Портфель {0} появился.", account); // если инструмент и стакан уже появились, // то извещаем об этом основной поток для выставления заявки if (_lkoh != null && _depth != null) waitHandle.Set(); } } }; // подписываемся на событие появление инструментов trader.NewSecurities += securities => { if (_lkoh == null) { // находим Лукойл и присваиваем ее переменной lkoh _lkoh = securities.FirstOrDefault(sec => sec.Code == secCode); if (_lkoh != null) { Console.WriteLine("Инструмент Лукойл появился."); // запускаем экспорт стакана trader.RegisterQuotes(_lkoh); if (_portfolio != null && _depth != null) waitHandle.Set(); } } }; // подписываемся на событие появления моих новых сделок trader.NewMyTrades += myTrades => { foreach (var myTrade in myTrades) { var trade = myTrade.Trade; Console.WriteLine("Сделка {0} по цене {1} по бумаге {2} по объему {3} в {4}.", trade.Id, trade.Price, trade.Security.Code, trade.Volume, trade.Time); } }; // подписываемся на событие обновления стакана trader.QuotesChanged += depths => { if (_depth == null && _lkoh != null) { _depth = depths.FirstOrDefault(d => d.Security == _lkoh); if (_depth != null) { Console.WriteLine("Стакан Лукойла появился."); // если портфель и инструмент уже появился, то извещаем об этом основной поток для выставления заявки if (_portfolio != null && _lkoh != null) waitHandle.Set(); } } }; Console.WriteLine("Дожидаемся появления в программе инструмента Лукойл и портфеля {0}...".Put(account)); // запускаем экспорт по DDE trader.StartExport(trader.SecuritiesTable, trader.MyTradesTable, trader.EquityPositionsTable, trader.EquityPortfoliosTable, trader.OrdersTable); // дожидаемся появления портфеля и инструмента waitHandle.WaitOne(); // 0.1% от изменения цены const decimal delta = 0.001m; // запоминаем первоначальное значение середины спреда var firstMid = _lkoh.BestPair.SpreadPrice / 2; if (_lkoh.BestBid == null) throw new Exception("Нет лучшего бида для котировки."); Console.WriteLine("Первоначальное значение середины спреда {0:0.##}", _lkoh.BestBid.Price + firstMid); while (true) { var mid = _lkoh.BestPair.SpreadPrice / 2; // если спред вышел за пределы нашего диапазона if ( ((firstMid + firstMid * delta) <= mid) || ((firstMid - firstMid * delta) >= mid) ) { var order = new Order { Portfolio = _portfolio, Price = _lkoh.ShrinkPrice(_lkoh.BestBid.Price + mid), Security = _lkoh, Volume = 1, Direction = OrderDirections.Buy, }; trader.RegisterOrder(order); Console.WriteLine("Заявка {0} зарегистрирована.", order.Id); break; } else Console.WriteLine("Текущее значение середины спреда {0:0.##}", _lkoh.BestBid.Price + mid); // ждем 1 секунду Thread.Sleep(1000); } // останавливаем экспорт по DDE trader.StopExport(trader.SecuritiesTable, trader.MyTradesTable, trader.EquityPositionsTable, trader.EquityPortfoliosTable, trader.OrdersTable); } } } catch (Exception ex) { Console.WriteLine(ex); } }
private void ClearCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) { SelectedPortfolio = null; }
static void Main() { try { // для теста выбираем бумагу Лукойл const string secCode = "LKOH"; Console.Write(LocalizedStrings.EnterLogin); var login = Console.ReadLine(); Console.Write(LocalizedStrings.EnterPassword); var password = Console.ReadLine(); Console.Write("Enter account number through which an order will be placed:".Translate()); var account = Console.ReadLine(); using (var waitHandle = new AutoResetEvent(false)) { // создаем подключение к Smart-у using (var trader = new SmartTrader { Login = login, Password = password, Address = SmartComAddresses.Demo }) { // подписываемся на событие успешного подключения // все действия необходимо производить только после подключения trader.Connected += () => { Console.WriteLine(LocalizedStrings.Str2169); // извещаем об успешном соединени waitHandle.Set(); }; Console.WriteLine(LocalizedStrings.Str2170); trader.Connect(); // дожидаемся события об успешном соединении waitHandle.WaitOne(); // подписываемся на все портфели-счета trader.NewPortfolios += portfolios => { if (_portfolio != null) return; // находим нужный портфель и присваиваем его переменной _portfolio _portfolio = portfolios.FirstOrDefault(p => p.Name == account); if (_portfolio == null) return; Console.WriteLine(LocalizedStrings.Str2171Params, account); if (_lkoh != null) waitHandle.Set(); }; // подписываемся на событие появление инструментов trader.NewSecurities += securities => { if (_lkoh == null) { // находим Лукойл и присваиваем ее переменной lkoh _lkoh = securities.FirstOrDefault(sec => sec.Code == secCode && sec.Type == SecurityTypes.Stock); if (_lkoh != null) { Console.WriteLine(LocalizedStrings.Str2987); if (_portfolio != null) waitHandle.Set(); } } }; // подписываемся на событие появления моих новых сделок trader.NewMyTrades += myTrades => { foreach (var myTrade in myTrades) { var trade = myTrade.Trade; Console.WriteLine(LocalizedStrings.Str2173Params, trade.Id, trade.Price, trade.Security.Code, trade.Volume, trade.Time); } }; Console.WriteLine(LocalizedStrings.Str2989Params.Put(account)); // дожидаемся появления портфеля и инструмента waitHandle.WaitOne(); trader.SecuritiesChanged += securities => { // если инструмент хоть раз изменился (по нему пришли актуальные данные) if (securities.Contains(_lkoh) && _lkoh.BestBid != null && _lkoh.BestAsk != null) waitHandle.Set(); }; Console.WriteLine("Waiting for Lukoil security data to update...".Translate()); // запускаем обновление по инструменту trader.RegisterSecurity(_lkoh); waitHandle.WaitOne(); // 0.1% от изменения цены const decimal delta = 0.001m; // запоминаем первоначальное значение середины спреда var firstMid = _lkoh.BestPair.SpreadPrice / 2; if (_lkoh.BestBid == null || firstMid == null) throw new Exception(LocalizedStrings.Str2990); Console.WriteLine(LocalizedStrings.Str2991Params, _lkoh.BestBid.Price + firstMid); while (true) { var mid = _lkoh.BestPair.SpreadPrice / 2; // если спред вышел за пределы нашего диапазона if (mid != null && ((firstMid + firstMid * delta) <= mid || (firstMid - firstMid * delta) >= mid) ) { var order = new Order { Portfolio = _portfolio, Price = _lkoh.ShrinkPrice(_lkoh.BestBid.Price + mid.Value), Security = _lkoh, Volume = 1, Direction = Sides.Buy, }; trader.RegisterOrder(order); Console.WriteLine(LocalizedStrings.Str1157Params, order.Id); break; } else Console.WriteLine(LocalizedStrings.Str2176Params, _lkoh.BestBid.Price + mid); // ждем 1 секунду Thread.Sleep(1000); } } } } catch (Exception ex) { Console.WriteLine(ex); } }
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 } }); }
static void Main() { try { // для теста выбираем бумагу Лукойл const string secCode = "LKOH"; var quikPath = QuikTerminal.GetDefaultPath(); if (quikPath.IsEmpty()) { Console.WriteLine(LocalizedStrings.Str2984); return; } Console.WriteLine(LocalizedStrings.Str2985 + quikPath); Console.Write(LocalizedStrings.Str2986); var account = Console.ReadLine(); using (var waitHandle = new AutoResetEvent(false)) { // создаем подключение к Quik-у using (var trader = new QuikTrader(quikPath) { IsDde = true }) { // необходимо раскомментировать, если идет работа с РТС Стандарт //trader.FormatTransaction += builder => builder.RemoveInstruction(Transaction.TimeInForce); // подписываемся на событие успешного подключения // все действия необходимо производить только после подключения trader.Connected += () => { Console.WriteLine(LocalizedStrings.Str2169); // извещаем об успешном соединени waitHandle.Set(); }; Console.WriteLine(LocalizedStrings.Str2170); trader.DdeTables = new[] { trader.SecuritiesTable, trader.MyTradesTable, trader.EquityPositionsTable, trader.EquityPortfoliosTable, trader.OrdersTable }; trader.Connect(); // дожидаемся события об успешном соединении waitHandle.WaitOne(); trader.NewPortfolios += portfolios => { if (_portfolio == null) { // находим нужный портфель и присваиваем его переменной _portfolio _portfolio = portfolios.FirstOrDefault(p => p.Name == account); if (_portfolio != null) { Console.WriteLine(LocalizedStrings.Str2171Params, account); // если инструмент и стакан уже появились, // то извещаем об этом основной поток для выставления заявки if (_lkoh != null && _depth != null) waitHandle.Set(); } } }; // подписываемся на событие появление инструментов trader.NewSecurities += securities => { if (_lkoh == null) { // находим Лукойл и присваиваем ее переменной lkoh _lkoh = securities.FirstOrDefault(sec => sec.Code == secCode); if (_lkoh != null) { Console.WriteLine(LocalizedStrings.Str2987); // запускаем экспорт стакана trader.RegisterMarketDepth(_lkoh); if (_portfolio != null && _depth != null) waitHandle.Set(); } } }; // подписываемся на событие появления моих новых сделок trader.NewMyTrades += myTrades => { foreach (var myTrade in myTrades) { var trade = myTrade.Trade; Console.WriteLine(LocalizedStrings.Str2173Params, trade.Id, trade.Price, trade.Security.Code, trade.Volume, trade.Time); } }; // подписываемся на событие обновления стакана trader.MarketDepthsChanged += depths => { if (_depth == null && _lkoh != null) { _depth = depths.FirstOrDefault(d => d.Security == _lkoh); if (_depth != null) { Console.WriteLine(LocalizedStrings.Str2988); // если портфель и инструмент уже появился, то извещаем об этом основной поток для выставления заявки if (_portfolio != null && _lkoh != null) waitHandle.Set(); } } }; Console.WriteLine(LocalizedStrings.Str2989Params.Put(account)); // дожидаемся появления портфеля и инструмента waitHandle.WaitOne(); // 0.1% от изменения цены const decimal delta = 0.001m; // запоминаем первоначальное значение середины спреда var firstMid = _lkoh.BestPair.SpreadPrice / 2; if (_lkoh.BestBid == null || firstMid == null) throw new Exception(LocalizedStrings.Str2990); Console.WriteLine(LocalizedStrings.Str2991Params, _lkoh.BestBid.Price + firstMid); while (true) { var mid = _lkoh.BestPair.SpreadPrice / 2; // если спред вышел за пределы нашего диапазона if (mid != null && ((firstMid + firstMid * delta) <= mid || (firstMid - firstMid * delta) >= mid) ) { var order = new Order { Portfolio = _portfolio, Price = _lkoh.ShrinkPrice(_lkoh.BestBid.Price + mid.Value), Security = _lkoh, Volume = 1, Direction = Sides.Buy, }; trader.RegisterOrder(order); Console.WriteLine(LocalizedStrings.Str1157Params, order.Id); break; } else Console.WriteLine(LocalizedStrings.Str2176Params, _lkoh.BestBid.Price + mid); // ждем 1 секунду Thread.Sleep(1000); } // останавливаем подключение trader.Disconnect(); } } } catch (Exception ex) { Console.WriteLine(ex); } }
private void LookupPortfolio(IEnumerable<Portfolio> portfolios) { _portfolio = portfolios.FirstOrDefault(port => port.Name == _strategyConfiguration.PortfolioName); if (_portfolio != null) { PortfolioFoundEvent.Set(); } }
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; }
/// <summary> /// Скопировать поля текущего портфеля в <paramref name="destination"/>. /// </summary> /// <param name="destination">Портфель, в который необходимо скопировать поля.</param> public void CopyTo(Portfolio destination) { base.CopyTo(destination); destination.Name = Name; destination.Board = Board; destination.Currency = Currency; destination.Leverage = Leverage; destination.Connector = Connector; destination.State = State; }
private void Button_Click(object sender, RoutedEventArgs e) { var wnd = new PortfolioPickerWindow { Connector = Connector, }; if (wnd.ShowModal(this)) { SelectedPortfolio = wnd.SelectedPortfolio; } }
//private void InitOrderLogBuilders(DateTime loadDate) //{ // if (StorageRegistry == null || !MarketEmulator.Settings.UseMarketDepth) // return; // foreach (var security in RegisteredMarketDepths) // { // var builder = _orderLogBuilders.TryGetValue(security); // if (builder == null) // continue; // // стакан из ОЛ строиться начиная с 18.45 предыдущей торговой сессии // var olDate = loadDate.Date; // do // { // olDate -= TimeSpan.FromDays(1); // } // while (!ExchangeBoard.Forts.WorkingTime.IsTradeDate(olDate)); // olDate += new TimeSpan(18, 45, 0); // foreach (var item in StorageRegistry.GetOrderLogStorage(security, Drive).Load(olDate, loadDate - TimeSpan.FromTicks(1))) // { // builder.Update(item); // } // } //} ///// <summary> ///// Найти инструменты, соответствующие фильтру <paramref name="criteria"/>. ///// </summary> ///// <param name="criteria">Инструмент, поля которого будут использоваться в качестве фильтра.</param> ///// <returns>Найденные инструменты.</returns> //public override IEnumerable<Security> Lookup(Security criteria) //{ // var securities = _historyAdapter.SecurityProvider.Lookup(criteria); // if (State == EmulationStates.Started) // { // foreach (var security in securities) // SendSecurity(security); // } // return securities; //} /// <summary> /// Subscribe on the portfolio changes. /// </summary> /// <param name="portfolio">Portfolio for subscription.</param> protected override void OnRegisterPortfolio(Portfolio portfolio) { _initialMoney.TryAdd(portfolio, portfolio.BeginValue); if (State == EmulationStates.Started) SendPortfolio(portfolio); }
private void RaiseNewPortfolio(Portfolio portfolio) { NewPortfolio.SafeInvoke(portfolio); var multiEvt = NewPortfolios; if (multiEvt == null) return; multiEvt.SafeInvoke(new[] { portfolio }); }
public Position TryAddPosition(Portfolio portfolio, Security security, string depoName, TPlusLimits? limitType, string description, out bool isNew) { isNew = false; Position position; lock (_positions.SyncRoot) { if (depoName == null) depoName = string.Empty; var key = Tuple.Create(portfolio, security, depoName, limitType); if (!_positions.TryGetValue(key, out position)) { isNew = true; position = EntityFactory.CreatePosition(portfolio, security); position.DepoName = depoName; position.LimitType = limitType; position.Description = description; _positions.Add(key, position); } } return position; }
private void RaisePortfolioChanged(Portfolio portfolio) { PortfolioChanged.SafeInvoke(portfolio); var multiEvt = PortfoliosChanged; if (multiEvt == null) return; multiEvt.SafeInvoke(new[] { portfolio }); }