← Zurück

Использование стакана при тестировании

Собственно есть две просьбы:

  1. подскажите пожалуйста, как стакан то добавить (возможно я сделал все верно, но я не уверен, тк впервые это делаю) в документации рассмотрен пример с генерацией стакана с интервалом в 1 сек или 1 милисек.
_trader.DepthGenerators[security] = new TrendMarketDepthGenerator(security)
{
    // стакан для инструмента в истории обновляется раз в секунду
    Interval = TimeSpan.FromSeconds(1)
};

я просто решил закомментить Interval = TimeSpan.FromSeconds(1), возможно не так сделал

  1. Как потом вызвать этот стакан в стратегии, в преведущем моем топике "takeprofit && stoploss" возможно кто-то помнит, сделано совсем не грамотно! и теперь думаю пришло время разобраться с этим.. Вот собственно весь код стратегии))

а)общий объем покупки пытался получить вот так MarketDepth.TotalBidsVolume(Security), вот так MarketDepth.TotalBidsVolume.Security , но увы ничего не вышло. Пишет "Невызываемый член "StockSharp.BusinessEntities.MarketDepth.TotalBidsVolume" не может использоваться как метод." в чем ошибка? б)как получить портфолио? я его по идее присваиваю при создании стратегии

_strategy = new NewStrategy()
			{
				Volume = 1,
				Portfolio = portfolio,
				Security = security,
				Trader = _trader
			};

те при создании заявки я могу просто убрать "Portfolio = portfolio," ?

var longPos = new Order
            {
                Portfolio = portfolio,
                Price = Security.ShrinkPrice(Security.BestBid.Price),
                Security = Security,
                Volume = 1,
                Direction = OrderDirections.Buy,
            };
    public class NewStrategy : Strategy
    {
        int i;
        protected override void OnStarting()
        {
            this
                .When(Security.MarketDepthChanged())
                .Do(str);

            base.OnStarting();
        }

        public void str()
        {

            var totb = MarketDepth.TotalBidsVolume(Security);// общий объем покупки
            var tota = _depth.TotalAsksVolume;// общий объем продажи

            if (_position.CurrentValue != 0) // если позиция не равна 0, то будем считать, что заявок у нас нету
            {
                i = 0;
            }

            if (i == 1) // если заявка есть, то...
            {
                if ((Security.BestBid.Price - posPrice >= 50 && pos == 0) || (posPrice - Security.BestAsk.Price >= 50 && pos == 1)) // если цена ушла от заявки на 50 и более пунктов то закрываем ее
                {
                    var orderClose = Trader.Orders.First(o => o.Id == posId);

                    /* получаем статус заявки */
                    int stateOrderClose = (int)orderClose.State;
                    /*
                     * 0 - Не отправлена в торговую систему.
                     * 1 - Заявка принята биржей и активна.
                     * 2 - Заявка больше не активна на бирже (была полностью удовлетворена или снята из программы). 
                     * 3 - Заявка не принята торговой системой.
                    */

                    if (stateOrderClose == 1)
                    {
                        Trader.CancelOrder(orderClose);
                    }
                    if (stateOrderClose == 2)
                    {
                        i = 0;
                    }
                }
            }

            if (_position.CurrentValue == 0 && i == 0)
            {
                if (totb > tota) // если общий объем покупок больше продаж, то продаем
                {
                    i = 1;
                    shortPos();
                }
                else
                {
                    i = 1;
                    longPos();
                }
            }
        }

        public void longPos()
        {
            // создаем заявку для открытия длинной позиции
            var longPos = new Order
            {
                Portfolio = portfolio,
                Price = Security.ShrinkPrice(Security.BestBid.Price),
                Security = Security,
                Volume = 1,
                Direction = OrderDirections.Buy,
            };

            // регистрируем правило, отслеживающее появление новых сделок по заявке
            this
            .When(longPos.NewTrades())
            .Do(OnNewOrderTrades)
            .Periodical(() => longPos.IsMatched());

            // отправляем заявку на регистрацию
            RegisterOrder(longPos);
            pos = 0;
            posPrice = Security.BestBid.Price;
        }

        public void shortPos()
        {
            // создаем заявку для открытия длинной позиции
            var shortPos = new Order
            {
                Portfolio = portfolio,
                Price = Security.ShrinkPrice(Security.BestAsk.Price),
                Security = Security,
                Volume = 1,
                Direction = OrderDirections.Sell,
            };

            // регистрируем правило, отслеживающее появление новых сделок по заявке
            this
            .When(shortPos.NewTrades())
            .Do(OnNewOrderTrades)
            .Periodical(() => shortPos.IsMatched());

            // отправляем заявку на регистрацию
            RegisterOrder(shortPos);
            pos = 1;
            posPrice = Security.BestAsk.Price;
        }

        private void OnNewOrderTrades(IEnumerable<MyTrade> trades)
        {
            // для каждой сделки добавляем для защитную пару стратегии
            var protectiveStrategies = trades.Select(t =>
            {
                // выставляет тейк-профит в 100 пунктов
                var takeProfit = new TakeProfitStrategy(t, 100);

                // выставляет стоп-лосс в 50 пунктов
                var stopLoss = new StopLossStrategy(t, 50);

                return new TakeProfitStopLossStrategy(takeProfit, stopLoss);
            });

            ChildStrategies.AddRange(protectiveStrategies);
        }
    }

В коде все равно останется много ошибок, но надеюсь я смогу с ними разобраться с Вашей помощью)) Спасибо за помощь

War dieses Thema hilfreich?

Thema teilen

Kommentare (10)

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

vader
vader
  1. б) Нет, нельзя убирать строку "Portfolio = portfolio," у заявки.
  2. а) сделай так - MarketDepth.TotalBidsVolume. Это же свойсто класса.

я просто решил закомментить Interval = TimeSpan.FromSeconds(1), Зачем?

if ((Security.BestBid.Price - posPrice >= 50 && pos == 0) || (posPrice - Security.BestAsk.Price >= 50 && pos == 1)) // если цена ушла от заявки на 50 и более пунктов то закрываем ее Замени использование числа 50 на переменную.


/* получаем статус заявки */                int stateOrderClose = (int)orderClose.State;                /*                 * 0 - Не отправлена в торговую систему.                 * 1 - Заявка принята биржей и активна.                 * 2 - Заявка больше не активна на бирже (была полностью удовлетворена или снята из программы).                  * 3 - Заявка не принята торговой системой.                */                 if (stateOrderClose == 1)                {                    Trader.CancelOrder(orderClose);                }                if (stateOrderClose == 2)                {                    i = 0;                }

Лучше сравнивай сам статус. Вот так.

if(_order.State == OrderStates.Done)
tmt
tmt Autor
  1. б) Нет, нельзя убирать строку "Portfolio = portfolio," у заявки.
  2. а) сделай так - MarketDepth.TotalBidsVolume. Это же свойсто класса.

я просто решил закомментить Interval = TimeSpan.FromSeconds(1), Зачем?

if ((Security.BestBid.Price - posPrice >= 50 && pos == 0) || (posPrice - Security.BestAsk.Price >= 50 && pos == 1)) // если цена ушла от заявки на 50 и более пунктов то закрываем ее Замени использование числа 50 на переменную.


/* получаем статус заявки */                int stateOrderClose = (int)orderClose.State;                /*                 * 0 - Не отправлена в торговую систему.                 * 1 - Заявка принята биржей и активна.                 * 2 - Заявка больше не активна на бирже (была полностью удовлетворена или снята из программы).                  * 3 - Заявка не принята торговой системой.                */                 if (stateOrderClose == 1)                {                    Trader.CancelOrder(orderClose);                }                if (stateOrderClose == 2)                {                    i = 0;                }

Лучше сравнивай сам статус. Вот так.

if(_order.State == OrderStates.Done)

если нельзя убирать портфолио, то как сделать то?

MarketDepth.TotalBidsVolume так таже самая ошибка..

зачем? я ведь не генерирую стаканы, а это как я понял интервал обновления (волотильность) инструмента.. не совсем понятно

"Замени использование числа 50 на переменную." это ньюансы, с ними проблем нету вроде как

vader
vader

"если нельзя убирать портфолио, то как сделать то?" Вопрос - что сделать?

_marketDepth.TotalBidsVolume.ToString() - такая строчка у меня вывела все что нужно. В данном случае MarketDepth это что, объект или класс?

""Замени использование числа 50 на переменную." это ньюансы, с ними проблем нету вроде как" Потом это приведит к трудноуловимым ошибкам, т.к. если в 3 местах нужно использовать одно число, к примеру 140, а потом приходиться менять его на 120 ,в одном месте можно забыть поменять и пол дня искать ошибку.

tmt
tmt
tmt Autor

"если нельзя убирать портфолио, то как сделать то?" Вопрос - что сделать? С Portfolio я разобрался, ошибка была в том, что писал с маленькой буквы 😊

_marketDepth.TotalBidsVolume.ToString() - такая строчка у меня вывела все что нужно. В данном случае MarketDepth это что, объект или класс? class у меня

Собственно мне кажется, что стакан у меня вовсе не создан.. Я не знаю, как мне его создать, а точнее исторически вытащить, в примере он не используется вовсе 🤨

""Замени использование числа 50 на переменную." это ньюансы, с ними проблем нету вроде как" Потом это приведит к трудноуловимым ошибкам, т.к. если в 3 местах нужно использовать одно число, к примеру 140, а потом приходиться менять его на 120 ,в одном месте можно забыть поменять и пол дня искать ошибку.

А за совет отдельное спасибо, я знал конечно, но пока я просто пытаюсь разобраться 🙂

vader
vader

Цитата: _marketDepth.TotalBidsVolume.ToString() - такая строчка у меня вывела все что нужно. В данном случае MarketDepth это что, объект или класс?

class у меня

Собственно мне кажется, что стакан у меня вовсе не создан.. Я не знаю, как мне его создать, а точнее исторически вытащить, в примере он не используется вовсе

Так понятно почему ошибка - поле то не статическое, нужно создать объект ,тогда можно будет получить суммарный объем. Как это сделать для тестинга на истории - я не знаю.

tmt
tmt Autor

В общем сделал вот так.

mainwindow.xaml.cs

namespace SampleHistoryTesting
{
	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 Ecng.Common;
	using Ecng.Serialization;
	using Ecng.Xaml;
	using Ecng.Collections;

	using StockSharp.Algo.Candles;
	using StockSharp.Algo.Reporting;
	using StockSharp.Algo.Storages;
	using StockSharp.Algo.Strategies;
	using StockSharp.Algo.Testing;
	using StockSharp.Algo.Logging;
	using StockSharp.Algo.Equity;
	using StockSharp.Algo.Indicators.Trend;
	using StockSharp.BusinessEntities;
	
	public partial class MainWindow
	{
		private Strategy _strategy;

		private ICollection<EquityData> _curveItems;
		private EmulationTrader _trader;

		private readonly LogManager _logManager = new LogManager();

		private DateTime _lastUpdateDate;
		private DateTime _startEmulationTime;

		public MainWindow()
		{
			InitializeComponent();

			_logManager.Listeners.Add(new FileLogListener("log.txt"));
		}

		private void FindPath_Click(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;
			}
		}

		private void StartBtn_Click(object sender, RoutedEventArgs e)
		{
			// если процесс был запущен, то его останавливаем
			if (_trader != null && _trader.State != EmulationStates.Stopped)
			{
				StartBtn.IsEnabled = false;

				_strategy.Stop();
				_trader.Stop();
				_logManager.Sources.Clear();

				return;
			}

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

			// создаем тестовый инструмент, на котором будет производится тестирование
			var security = new Security
			{
				Id = "RIH2@RTS", // по идентификатору инструмента будет искаться папка с историческими маркет данными
				Code = "RIH2",
				Name = "RTS-3.12",
				MinStepSize = 5,
				MinStepPrice = 2,
				Exchange = Exchange.Test,
			};

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

			// хранилище, через которое будет производиться доступ к тиковой и котировочной базе
			var storage = new TradingStorage(new InMemoryStorage())
			{
				BasePath = HistoryPath.Text
			};

			var timeFrame = TimeSpan.FromMinutes(5);

			// в реальности период может быть другим, и это зависит от объема данных,
			// хранящихся по пути HistoryPath, 
			var startTime = new DateTime(2012, 2, 27);
            var stopTime = new DateTime(2012, 2, 28);
	
			_trader = new EmulationTrader(
				new[] { security },
				new[] { portfolio })
			{
				MarketTimeChangedInterval = timeFrame,
				Storage = storage,
				WorkingTime = Exchange.Rts.WorkingTime,

				// параметр влияет на занимаемую память.
				// в случае достаточно количества памяти на компьютере рекомендуется его увеличить
				DaysInMemory = 6,
			};
			
			_trader.DepthGenerators[security] = new TrendMarketDepthGenerator(security)
			{
				// стакан для инструмента в истории обновляется раз в секунду
				Interval = TimeSpan.FromSeconds(1),
			};

			var candleManager = new CandleManager();
			var builder = new CandleBuilder(new TradeCandleBuilderSource(_trader) { IsSyncProcess = true });
			candleManager.Sources.Add(builder);

			// в целях оптимизации расходования памяти храним не более 100 последних свечек и 100000 последних сделок
			((CandleContainer)candleManager.Container).MaxCandleCount = 100;
			((CandleBuilderContainer)builder.Container).MaxCandleCount = 100;
			((CandleBuilderContainer)builder.Container).MaxValueCount = 100000;

			candleManager.RegisterTimeFrameCandles(security, timeFrame);

			// создаем торговую стратегию, скользящие средние на 80 5-минуток и 10 5-минуток
            _strategy = new NewStrategy()
			{
				Volume = 1,
				Portfolio = portfolio,
				Security = security,
				Trader = _trader
			};

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

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

			_strategy.EquityManager.NewEquityData += data => this.GuiAsync(() => _curveItems.Add(data));

			_logManager.Sources.Add(_strategy);

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

			_trader.StateChanged += () =>
			{
				if (_trader.State == EmulationStates.Stopped)
				{
					this.GuiAsync(() =>
					{
						StartBtn.IsEnabled = true;

						if (TestingProcess.Value == TestingProcess.Maximum)
							MessageBox.Show("Закончено за " + (DateTime.Now - _startEmulationTime));
						else
							MessageBox.Show("Отменено");
					});
				}
				else if (_trader.State == EmulationStates.Started)
				{
					// запускаем стратегию когда эмулятор запустился
					_strategy.Start();
				}
			};

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

			_startEmulationTime = DateTime.Now;

			// соединяемся с трейдером и запускаем экспорт,
			// чтобы инициализировать переданными инструментами и портфелями необходимые свойства EmulationTrader
			_trader.Connect();
			_trader.StartExport();

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

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

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

strategy.cs

namespace SampleHistoryTesting
{
	using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;

    using Ecng.Common;
    using Ecng.Collections;

	using StockSharp.Algo;
	using StockSharp.Algo.Candles;
	using StockSharp.Algo.Indicators;
	using StockSharp.Algo.Indicators.Trend;
	using StockSharp.Algo.Strategies;
	using StockSharp.BusinessEntities;

    public class NewStrategy : Strategy
    {
        static int pos = 2; // 0 - buy; 1 - sell
        static decimal posPrice = 0;
        static long posId;
        int i;
        protected override void OnStarting()
        {
            this
                .When(Security.MarketDepthChanged())
                .Do(str);

            base.OnStarting();
        }

        private static MarketDepth _marketDepth;
        private static Position _position;

        public void str()
        {
            Trader.QuotesChanged += depths =>
            {
                _marketDepth = depths.FirstOrDefault(d => d.Security == Security);
            };

            Trader.NewPositions += positions =>
            {
                if (_position == null)
                {
                    _position = positions.FirstOrDefault();
                }
            };

            Trader.NewOrders += myTrades =>
            {
                foreach (var myTrade in myTrades)
                {
                    var trade = myTrade;
                    //Console.WriteLine("Сделка {0} по цене {1} по бумаге {2} по объему {3} в {4}.{5}", trade.Id, trade.Price, trade.Security.Code, trade.Volume, trade.Time, trade.Direction);
                    int _pos = (int)trade.Direction;
                    if (posPrice == trade.Price && pos == _pos)
                    {
                        posId = trade.Id;
                    }
                }
            };

            var totb = _marketDepth.TotalBidsVolume;// общий объем покупки
            var tota = _marketDepth.TotalAsksVolume;// общий объем продажи

            if (_position.CurrentValue != 0) // если позиция не равна 0, то будем считать, что заявок у нас нету
            {
                i = 0;
            }

            if (i == 1) // если заявка есть, то...
            {
                if ((Security.BestBid.Price - posPrice >= 50 && pos == 0) || (posPrice - Security.BestAsk.Price >= 50 && pos == 1)) // если цена ушла от заявки на 50 и более пунктов то закрываем ее
                {
                    var orderClose = Trader.Orders.First(o => o.Id == posId);

                    /* получаем статус заявки */
                    int stateOrderClose = (int)orderClose.State;
                    /*
                     * 0 - Не отправлена в торговую систему.
                     * 1 - Заявка принята биржей и активна.
                     * 2 - Заявка больше не активна на бирже (была полностью удовлетворена или снята из программы). 
                     * 3 - Заявка не принята торговой системой.
                    */

                    if (stateOrderClose == 1)
                    {
                        Trader.CancelOrder(orderClose);
                    }
                    if (stateOrderClose == 2)
                    {
                        i = 0;
                    }
                }
            }

            if (_position.CurrentValue == 0 && i == 0)
            {
                if (totb > tota) // если общий объем покупок больше продаж, то продаем
                {
                    i = 1;
                    shortPos();
                }
                else
                {
                    i = 1;
                    longPos();
                }
            }
        }

        public void longPos()
        {
            // создаем заявку для открытия длинной позиции
            var longPos = new Order
            {
                Portfolio = Portfolio,
                Price = Security.ShrinkPrice(Security.BestBid.Price),
                Security = Security,
                Volume = 1,
                Direction = OrderDirections.Buy,
            };

            // регистрируем правило, отслеживающее появление новых сделок по заявке
            this
            .When(longPos.NewTrades())
            .Do(OnNewOrderTrades)
            .Periodical(() => longPos.IsMatched());

            // отправляем заявку на регистрацию
            RegisterOrder(longPos);
            pos = 0;
            posPrice = Security.BestBid.Price;
        }

        public void shortPos()
        {
            // создаем заявку для открытия длинной позиции
            var shortPos = new Order
            {
                Portfolio = Portfolio,
                Price = Security.ShrinkPrice(Security.BestAsk.Price),
                Security = Security,
                Volume = 1,
                Direction = OrderDirections.Sell,
            };

            // регистрируем правило, отслеживающее появление новых сделок по заявке
            this
            .When(shortPos.NewTrades())
            .Do(OnNewOrderTrades)
            .Periodical(() => shortPos.IsMatched());

            // отправляем заявку на регистрацию
            RegisterOrder(shortPos);
            pos = 1;
            posPrice = Security.BestAsk.Price;
        }

        private void OnNewOrderTrades(IEnumerable<MyTrade> trades)
        {
            // для каждой сделки добавляем для защитную пару стратегии
            var protectiveStrategies = trades.Select(t =>
            {
                // выставляет тейк-профит в 100 пунктов
                var takeProfit = new TakeProfitStrategy(t, 100);

                // выставляет стоп-лосс в 50 пунктов
                var stopLoss = new StopLossStrategy(t, 50);

                return new TakeProfitStopLossStrategy(takeProfit, stopLoss);
            });

            ChildStrategies.AddRange(protectiveStrategies);
        }
    }
}

Но увы не робит... я считаю что со стаканом что то не так..

vader
vader

Покажи место, где происходит инициализация стакана.

tmt
tmt Autor

vader: Покажи место, где происходит инициализация стакана.

Trader.QuotesChanged += depths =>
            {
                _marketDepth = depths.FirstOrDefault(d => d.Security == Security);
            };
vader
vader

Честно - ХЗ почему не работает.

Я бы взял один из примеров и потишоньку бы передолываал его под себя, тогда будет видна причина ошибки. И Станет понятен мехнизм работы S#.

tmt
tmt Autor

vader: Честно - ХЗ почему не работает.

Я бы взял один из примеров и потишоньку бы передолываал его под себя, тогда будет видна причина ошибки. И Станет понятен мехнизм работы S#. Да я вроде так и делаю, лан) начну сначала.