← Zurück

Проба пера: SampleSpreadHistoryTesting

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

MainWindow.xaml.cs

namespace SampleSpreadHistoryTesting
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Windows;
    using System.Windows.Forms;
    using System.Windows.Media;
    using MessageBox = System.Windows.MessageBox;
    using StockSharp.Algo.Equity;
    using StockSharp.Algo.Logging;
    using StockSharp.Algo.Reporting;
    using StockSharp.Algo.Storages;
    using StockSharp.Algo.Strategies;
    using StockSharp.Algo.Testing;
    using StockSharp.BusinessEntities;
    using Ecng.Collections;
    using Ecng.Common;
    using Ecng.Serialization;
    using Ecng.Xaml;

    public partial class MainWindow
    {
        private ICollection<EquityData> _curveItems;
        private readonly LogManager _logManager = new LogManager();
        private DateTime _lastUpdateDate;
        private DateTime _startEmulationTime;

        public void LogManager()
        {
            InitializeComponent();
            _logManager.Listeners.Add(new FileLogListener("log.txt"));
        }

        private void FindPathClick(object sender, RoutedEventArgs e)
        {
            var dlg = new FolderBrowserDialog();

            if (!HistoryPath.Text.IsEmpty())
                dlg.SelectedPath = HistoryPath.Text;

            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                HistoryPath.Text = dlg.SelectedPath;
            }
        }

        public void StartBtnClick(object sender, RoutedEventArgs e)
        {
            // создаем хранилище для работы с базой данных котировок
            var storage =
                new TradingStorage(new InMemoryStorage())
                    {
                        BasePath = HistoryPath.Text
                    };

            if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text))
            {
                MessageBox.Show(this, "Неправильный путь");
                return;
            }

            // выбираем интервал времени для тестирования (таймфрейм)
            var timeFrame = TimeSpan.FromTicks(1);

            // устанавливаем период тестирования (зависит от объема данных, хранящихся по пути HistoryPath) 
            var startTime = new DateTime(2012, 1, 1);
            var stopTime = new DateTime(2012, 2, 1);

            // создаем тестовый портфель
            var portfolio =
                new Portfolio
                    {
                        BeginAmount = 1000000m,
                        Name = "Test account"
                    };

            // создаем первый инструмент спреда
            var security1 =
                new Security
                    {
                        Code = "SBER",
                        Exchange = Exchange.Test,
                        // идентификатор инструмента, по которому будет осуществлен поиск папки с историей стаканов
                        Id = "SBER@EQBR",
                        MinStepPrice = 0.01m,
                        MinStepSize = 0.01m,
                        Name = "Сбербанк России ОАО ао"
                    };

            // создаем второй инструмент спреда
            var security2 =
                new Security
                    {
                        Code = "SRH2",
                        Exchange = Exchange.Test,
                        // идентификатор инструмента, по которому будет осуществлен поиск папки с историей стаканов
                        Id = "SRH2@RTS",
                        MinStepPrice = 1,
                        MinStepSize = 1,
                        Name = "SBRF-3.12"
                    };

            // создаем стратегию для торговли спредом
            var spreadStrategy =
                new SpreadStrategy(security1, security2)
                    {
                        Security1 = security1,
                        Security2 = security2
                    };
            
            // создаем шлюз для торговли
            var trader =
                new EmulationTrader(new[] {security1, security2}, new[] {portfolio})
                    {
                        // параметр влияет на занимаемую память (в случае достаточного объема памяти лучше увеличить)
                        DaysInMemory = 2,
                        MarketTimeChangedInterval = timeFrame,
                        Storage = storage,
                        WorkingTime = Exchange.Micex.WorkingTime
                    };

            // если процесс был запущен, останавливаем его
            if (trader.State != EmulationStates.Stopped)
            {
                StartBtn.IsEnabled = false;
                spreadStrategy.Stop();
                trader.Stop();
                _logManager.Sources.Clear();
                return;
            }

            trader.DepthGenerators[security1] =
                new TrendMarketDepthGenerator(security1)
                    {
                        // время обновления стакана в базе данных для первого инструмента
                        Interval = TimeSpan.FromTicks(1)
                    };

            trader.DepthGenerators[security2] =
                new TrendMarketDepthGenerator(security2)
                    {
                        // время обновления стакана в базе данных для второго инструмента
                        Interval = TimeSpan.FromTicks(1)
                    };

            // копируем параметры на визуальную панель
            ParametersPanel.Parameters.Clear();
            ParametersPanel.Parameters.AddRange(spreadStrategy.EquityManager.Parameters);

            if (_curveItems == null)
                _curveItems = Curve.CreateCurve(spreadStrategy.Name, Colors.DarkGreen);
            else
            _curveItems.Clear();

            spreadStrategy.EquityManager.NewEquityData += data => this.GuiAsync(() => _curveItems.Add(data));
            _logManager.Sources.Add(spreadStrategy);

            // подписываемся на событие изменения времени, чтобы обновить ProgressBar
            trader.MarketTimeChanged +=
                () =>
                    {
                        // для оптимизации обновляем ProgressBar только при начале нового дня
                        if (trader.MarketTime.Date == _lastUpdateDate && trader.MarketTime < stopTime) return;
                        _lastUpdateDate = trader.MarketTime.Date;
                        this.GuiAsync(() => TestingProcess.Value = (trader.MarketTime - startTime).Ticks);
                    };

            trader.StateChanged +=
                () =>
                    {
                        switch (trader.State)
                        {
                            case EmulationStates.Stopped:
                                this.GuiAsync(
                                    () =>
                                        {
                                            StartBtn.IsEnabled = true;
                                            if (Convert.ToDecimal(TestingProcess.Value) ==
                                                Convert.ToDecimal(TestingProcess.Maximum))
                                                MessageBox.Show("Закончено за " + (DateTime.Now - _startEmulationTime));
                                            else
                                            MessageBox.Show("Отменено");
                                        });
                                break;
                            case EmulationStates.Started:
                                // запускаем стратегию, когда эмулятор запустился
                                spreadStrategy.Start();
                                break;
                        }
                    };

            // устанавливаем в визуальный элемент ProgressBar максимальное количество итераций
            TestingProcess.Maximum = (stopTime - startTime).Ticks;
            TestingProcess.Value = 0;
            Report.IsEnabled = true;

            _startEmulationTime = DateTime.Now;

            // устанавливаем соединение и запускаем экспорт
            trader.Connect();
            trader.StartExport();

            // задаем период тестирования (startTime, stopTime) и запускаем эмуляцию
            trader.Start(startTime, stopTime);
        }

        private void ReportClick(object sender, RoutedEventArgs e)
        {
            // генерируем отчет по прошедшему тестированию (Excel может тормозить из-за большого количества данных)
            new ExcelStrategyReport(ExcelStrategy, "sma.xls").Generate();

            // открываем отчет
            Process.Start("sma.xls");
        }

        public Strategy ExcelStrategy { get; set; }
    }
}

SpreadStrategy.cs

namespace SampleSpreadHistoryTesting
{
    using System;
    using StockSharp.Algo;
    using StockSharp.Algo.Strategies;
    using StockSharp.BusinessEntities;

    class SpreadStrategy : Strategy
    {
        public decimal SpreadSellDelta;
        public decimal SpreadBuyDelta;
        public Security Security1 = new Security();
        public Security Security2 = new Security();

        public SpreadStrategy(Security security1, Security security2)
        {
            Security1 = security1;
            Security2 = security2;
        }

        protected override void  OnStarting()
        {
            decimal priceRatio;

            if (Security2.MinStepPrice > Security1.MinStepPrice)
                // вычисляем соотношение цен инструментов
                priceRatio = Security2.MinStepPrice/Security1.MinStepPrice;
            else
            priceRatio = Security1.MinStepPrice/Security2.MinStepPrice;

            // для продажи спреда вычисляем разность между ценой покупки второго актива и ценой продажи первого
            SpreadSellDelta = Convert.ToDecimal(Security2.BestAsk) - Convert.ToDecimal(Security1.BestBid)*priceRatio;
            
            // для покупки спреда вычисляем разность между ценой продажи второго актива и ценой покупки первого
            SpreadBuyDelta = Convert.ToDecimal(Security2.BestBid) - Convert.ToDecimal(Security1.BestAsk)*priceRatio;

            base.OnStarting();
        }

        protected ProcessResults OnProcess()
        {
            // если наша стратегия в процессе остановки
			if (ProcessState == ProcessStates.Stopping)
			{
				// отменяем активные заявки
				CancelActiveOrders();

				// так как все активные заявки гарантированно были отменены, то возвращаем ProcessResults.Stop
				return ProcessResults.Stop;
			}

            // задаем интервал для торговли спредом
            const decimal spreadSellPrice = 100m;
            const decimal spreadBuyPrice = -100m;

            // проверяем сигнал на продажу спреда
            if (SpreadSellDelta < spreadSellPrice) return ProcessResults.Continue;
            {
                // продажа спреда
                const OrderDirections direction1 = OrderDirections.Buy;
                const OrderDirections direction2 = OrderDirections.Sell;

                // регистрируем заявки
                var orderSecurity1 = this.CreateOrder(direction1, Security1.GetMarketPrice(direction1), 100m);
                RegisterOrder(orderSecurity1);
                var orderSecurity2 = this.CreateOrder(direction2, Security2.GetMarketPrice(direction2), 1m);
                RegisterOrder(orderSecurity2);
            }

            // проверяем сигнал на покупку спреда
            if (SpreadBuyDelta > spreadBuyPrice) return ProcessResults.Continue;
            {
                // покупка спреда
                const OrderDirections direction1 = OrderDirections.Sell;
                const OrderDirections direction2 = OrderDirections.Buy;

                // регистрируем заявки
                var orderSecurity1 = this.CreateOrder(direction1, Security1.GetMarketPrice(direction1), 100m);
                RegisterOrder(orderSecurity1);
                var orderSecurity2 = this.CreateOrder(direction2, Security2.GetMarketPrice(direction2), 1m);
                RegisterOrder(orderSecurity2);
            }

            return ProcessResults.Continue;
        }
    }
}

MainWindow.xaml


<Window x:Class="SampleSpreadHistoryTesting.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:StockSharp.Xaml;assembly=StockSharp.Xaml" Title="Тестирование по стаканам" Height="300" Width="525">
    
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="15*" />
            <ColumnDefinition Width="85*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid Grid.ColumnSpan="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="10" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="10" />
            </Grid.RowDefinitions>

            <Label Grid.Column="0" Grid.Row="0" Content="Путь к истории:" />
            <TextBox x:Name="HistoryPath" Text="" Grid.Column="1" Grid.Row="0" />
            <Button x:Name="FindPath" Grid.Column="2" Grid.Row="0" Content="..." Width="25" HorizontalAlignment="Left" Click="FindPathClick" />

            <Button x:Name="StartBtn" Content="Старт" Grid.Row="2" Click="StartBtnClick" />
            <ProgressBar x:Name="TestingProcess" Grid.Column="1" Grid.Row="2" />

            <Button x:Name="Report" Content="Отчет" Grid.Row="2" Width="75" IsEnabled="False" Click="ReportClick" Grid.Column="2" Margin="0,0,0,-1" />
        </Grid>
        <Grid Grid.Row="2" Grid.ColumnSpan="2">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="180"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <my:EquityParameterPanel Grid.Column="0" x:Name="ParametersPanel" />
                <my:EquityCurveChart Grid.Column="1" x:Name="Curve" />
            </Grid>
        </Grid>
        
    </Grid>
</Window>

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

Список нерешенных проблем:

  1. При запуске приложения не создается лог-файл
  2. Не производится тестирование
  3. Не генерируется отчет по тестированию
  4. Нет возможности построить график спреда для дальнейшего анализа
  5. Необходимо подробное условие для входа и выхода из позиции (на основании графика спреда)
  6. Проверка текущего лимита средств, исполненных заявок (расчет суммарной дельты портфеля)
  7. Переменные для указания количества ценных бумаг в заявке

War dieses Thema hilfreich?

Thema teilen

Kommentare (4)

Anmelden oder Konto erstellen, Melden Sie sich an oder registrieren Sie sich, um einen Kommentar zu hinterlassen

freelancer
freelancer

Что за рекурсия spreadStrategy.ChildStrategies.Add(spreadStrategy) ?

JackSparrow
JackSparrow

Вроде как бизнес логику надо прописать в классе создавнной стратегии а не в методе обработчика клика ( MainWindow.StartBtnClik ). По клику создаем и запускаем а потом уже трейдер сам со стратегией общается.

Это первое что увидел.

freelancer
freelancer

Да.Оболочка + Стратегии

ingeniero
ingeniero Autor

freelancer, JackSparrow, спасибо. Убрал ChildStrategies, вынес стратегию в отдельный класс. К сожалению, на работоспособность приложения это не повлияло:)