namespace SampleSMA { using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.ComponentModel; using System.Globalization; using System.IO; using System.Threading; using System.Windows; using System.Windows.Forms; using MessageBox = System.Windows.MessageBox; using AmCharts.Windows.Stock; using Ecng.Collections; using Ecng.Common; using Ecng.Xaml; using Ecng.ComponentModel; using StockSharp.Algo; using StockSharp.Algo.Logging; using StockSharp.Algo.Candles; using StockSharp.Algo.Reporting; using StockSharp.Algo.Indicators; using StockSharp.Algo.Indicators.Trend; using StockSharp.BusinessEntities; using StockSharp.Quik; using StockSharp.Xaml; public partial class MainWindow { private readonly TimeSpan _timeFrame = TimeSpan.FromMinutes(1); private QuikTrader _trader; private SmaStrategy _strategy; private bool _isDdeStarted; private DateTime _lastCandleTime; private bool _isTodaySmaDrawn; private CandleManager _candleManager; private readonly ICollection _longSmaGraph; private readonly ICollection _shortSmaGraph; private Security _lkoh; public MainWindow() { InitializeComponent(); // изменяет текущий формат, чтобы нецелое числа интерпритировалось как разделенное точкой. var cci = new CultureInfo(Thread.CurrentThread.CurrentCulture.Name) { NumberFormat = { NumberDecimalSeparator = "." } }; Thread.CurrentThread.CurrentCulture = cci; _longSmaGraph = _chart.CreateTrend("Длинная", GraphType.Line); _shortSmaGraph = _chart.CreateTrend("Короткая", GraphType.Line); // попробовать сразу найти месторасположение Quik по запущенному процессу Path.Text = QuikTerminal.GetDefaultPath(); } private void OrdersOrderSelected(object sender, EventArgs e) { CancelOrders.IsEnabled = !_orders.SelectedOrders.IsEmpty(); } protected override void OnClosing(CancelEventArgs e) { if (_trader != null) { if (_isDdeStarted) StopDde(); _trader.Dispose(); } base.OnClosing(e); } private void FindPathClick(object sender, RoutedEventArgs e) { var dlg = new FolderBrowserDialog(); if (!Path.Text.IsEmpty()) dlg.SelectedPath = Path.Text; if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { Path.Text = dlg.SelectedPath; } } private void ConnectClick(object sender, RoutedEventArgs e) { if (_trader == null || !_trader.IsConnected) { if (_trader == null) { if (Path.Text.IsEmpty()) { MessageBox.Show(this, "Путь к Quik не выбран."); return; } // создаем шлюз _trader = new QuikTrader(Path.Text); Portfolios.Trader = _trader; _trader.Connected += () => { _candleManager = new CandleManager(_trader); _trader.NewSecurities += securities => this.GuiAsync(() => { // находим нужную бумагу var lkoh = securities.FirstOrDefault(s => s.Code == "RIU2"); if (lkoh != null) { _lkoh = lkoh; this.GuiAsync(() => { Start.IsEnabled = true; }); } }); _trader.NewMyTrades += trades => this.GuiAsync(() => { if (_strategy != null) { // найти те сделки, которые совершила стратегия скользящей средней trades = trades.Where(t => _strategy.Orders.Any(o => o == t.Order)); _trades.Trades.AddRange(trades); } }); _candleManager.Processing += (series, candle) => { DrawCandles(new[] { candle }); // если скользящие за сегодняшний день отрисованы, то рисуем в реальном времени текущие скользящие if (_isTodaySmaDrawn) DrawSma(series); }; //_trader.ProcessDataError += ex => this.Sync(() => MessageBox.Show(this, ex.ToString())); _trader.ConnectionError += ex => { if (ex != null) this.GuiAsync(() => MessageBox.Show(this, ex.ToString())); }; this.GuiAsync(() => { ConnectBtn.IsEnabled = false; ExportDde.IsEnabled = true; Report.IsEnabled = true; }); }; } _trader.Connect(); } else _trader.Disconnect(); } private void OnNewOrder(Order order) { _orders.Orders.Add(order); this.GuiAsync(() => _chart.Orders.Add(order)); } private void OnLog(LogMessage message) { // если стратегия вывела не просто сообщение, то вывести на экран. if (message.Type != ErrorTypes.None) this.GuiAsync(() => MessageBox.Show(this, message.Message)); } private void DrawCandles(IEnumerable candles) { this.GuiAsync(() => _chart.Candles.AddRange(candles)); } private void DrawSma(CandleSeries series) { // нас не интересует текущая свечка, так как она еще не сформировалась // и из нее нельзя брать цену закрытия // вычисляем временные отрезки текущей свечки var bounds = _timeFrame.GetCandleBounds(_trader); // если появились новые полностью сформированные свечки if ((_lastCandleTime + _timeFrame) < bounds.Min) { // отступ с конца интервала, чтобы не захватить текущую свечку. var endOffset = TimeSpan.FromSeconds(1); bounds = new Range(_lastCandleTime + _timeFrame, bounds.Min - endOffset); // получаем эти свечки var candles = _candleManager.Container.GetCandles(series); if (!candles.IsEmpty()) { foreach (var candle in candles) { // добавляем новую свечку _strategy.LongSma.Process((DecimalIndicatorValue)candle.ClosePrice); _strategy.ShortSma.Process((DecimalIndicatorValue)candle.ClosePrice); } // получаем время самой последней свечки и запоминаем его как новое начало _lastCandleTime = candles.Max(c => c.OpenTime); DrawSmaLines(bounds.Min); } } } private void DrawSmaLines(DateTime time) { this.GuiSync(() => { _longSmaGraph.Add(new CustomChartIndicator { Time = time, Value = (double)_strategy.LongSma.LastValue }); _shortSmaGraph.Add(new CustomChartIndicator { Time = time, Value = (double)_strategy.ShortSma.LastValue }); }); } private void OnStrategyPropertyChanged(object sender, PropertyChangedEventArgs e) { this.GuiAsync(() => { Status.Content = _strategy.ProcessState; PnL.Content = _strategy.PnLManager.PnL; Slippage.Content = _strategy.SlippageManager.Slippage; Position.Content = _strategy.PositionManager.Position; Latency.Content = _strategy.LatencyManager.Latency; }); } private void StartDde() { _trader.StartExport(); _isDdeStarted = true; } private void StopDde() { _trader.StopExport(); _isDdeStarted = false; } private void ExportDdeClick(object sender, RoutedEventArgs e) { if (_isDdeStarted) StopDde(); else StartDde(); } private void CancelOrdersClick(object sender, RoutedEventArgs e) { _orders.SelectedOrders.ForEach(_trader.CancelOrder); } private void StartClick(object sender, RoutedEventArgs e) { if (_strategy == null) { if (Portfolios.SelectedPortfolio == null) { MessageBox.Show(this, "Портфель не выбран."); return; } var candles = File.ReadAllLines("LKOH_history.txt").Select(line => { var parts = line.Split(','); var time = (parts[0] + parts[1]).ToDateTime("yyyyMMddHHmmss"); return (Candle)new TimeFrameCandle { OpenPrice = parts[2].To(), HighPrice = parts[3].To(), LowPrice = parts[4].To(), ClosePrice = parts[5].To(), TimeFrame = _timeFrame, OpenTime = time, CloseTime = time + _timeFrame, TotalVolume = parts[6].To(), Security = _lkoh, }; }); DrawCandles(candles); // регистрируем наш тайм-фрейм var series = new CandleSeries(typeof(TimeFrameCandle), _lkoh, _timeFrame); // создаем торговую стратегию, скользящие средние на 80 5-минуток и 10 5-минуток _strategy = new SmaStrategy(series, new SimpleMovingAverage { Length = 80 }, new SimpleMovingAverage { Length = 10 }) { Volume = 1, Security = _lkoh, Portfolio = Portfolios.SelectedPortfolio, Trader = _trader, }; _strategy.Log += OnLog; _strategy.NewOrder += OnNewOrder; _strategy.PropertyChanged += OnStrategyPropertyChanged; var index = 0; // начинаем вычислять скользящие средние foreach (var candle in candles) { _strategy.LongSma.Process((DecimalIndicatorValue)candle.ClosePrice); _strategy.ShortSma.Process((DecimalIndicatorValue)candle.ClosePrice); // если все скользящие сформировались, то начинаем их отрисовывать if (index >= _strategy.LongSma.Length) DrawSmaLines(candle.OpenTime); index++; _lastCandleTime = candle.OpenTime; } _candleManager.Start(series); // вычисляем временные отрезки текущей свечки var bounds = _timeFrame.GetCandleBounds(_trader); candles = _candleManager.Container.GetCandles(series, new Range(_lastCandleTime + _timeFrame, bounds.Min)); foreach (var candle in candles) { _strategy.LongSma.Process((DecimalIndicatorValue)candle.ClosePrice); _strategy.ShortSma.Process((DecimalIndicatorValue)candle.ClosePrice); DrawSmaLines(candle.OpenTime); _lastCandleTime = candle.OpenTime; } _isTodaySmaDrawn = true; Report.IsEnabled = true; } if (_strategy.ProcessState == ProcessStates.Stopped) { // запускаем процесс получения стакана, необходимый для работы алгоритма котирования _trader.RegisterMarketDepth(_strategy.Security); _strategy.Start(); Start.Content = "Стоп"; } else { _trader.UnRegisterMarketDepth(_strategy.Security); _strategy.Stop(); Start.Content = "Старт"; } } private void ReportClick(object sender, RoutedEventArgs e) { // сгерерировать отчет по прошедшему тестированию new ExcelStrategyReport(_strategy, "sma.xls").Generate(); // открыть отчет Process.Start("sma.xls"); } } }