S#.Shell. Manual
Atom
10/14/2013


Часть 1. Starting up


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

Уже есть Альфадирект и Квик. Мне бы хотелось подрубить свой кастомный для IB. Но незадача: В каркасе старая версия S#.
Это грозит проблемами в работе с гидрой и коннекторами. У меня возникли сразу, потому что коннектор был на 4.1.19.1.

Значит заодно и обновлюсь.

Удаляю все, что связано со stocksharp и Ecng из reference, и добавляю заново все свежее.
У меня вот такой список получился.

1


После обновления, ничего не заработает.

Поправил в ConnectionEngine для квика и для альфа аналогично.

2


После ребилда все должно взлететь.

Добавил, естественно, нужные Refercences, и создание коннектора по аналогии.

3

4


Настройки. Тут есть целый менеджер настроек, который отвечает за сохранение, и т.д.

Добавил в список коннекторов и нужные настройки в SettingsProperties


5

6


Ну вот и все.


7

7





< 1 2 3  >
Aton5

Avatar
Date: 1/15/2014
Reply


где взять исходники S#.Shell, тем у кого лицензия это позволяет?
Дайте, пожалуйста, ссылку.
Thanks:

dij1

Avatar
Date: 1/18/2014
Reply


скачал версию 4.2.2, там вообще Shell не открывается, библиотек основных нет.
Thanks:

JaguarFX

Avatar
Date: 2/12/2014
Reply


Месяц прошел - и молчание разработчиков!))
На самом деле Shell действительно достаточно сырой продукт, в который необходимо много вложить чтобы получить выхлоп.
Даже в версии от Казакова Сергея достаточно ошибок и неточностей, на исправление которых у меня ушло множество времени.

Так почти месяц назад я поставил для себя цель провести миграцию своих торговых роботов с платформы TradeMatic на платформу S#.Shell, и за это время наработал материал, которым стоит поделиться дабы не возникали подобные вопросы.
Thanks:

JaguarFX

Avatar
Date: 2/12/2014
Reply


Часть 4. Универсализация создания, тестирования и исполнения стратегий

Первое что мне бросилось в глаза в версии от Kazai Mazai, которую я продолжил допиливать - отсутствие универсальности в исполнении/тестировании одной и той же стратегии.
Так если запустить стратегию RobotStrategy на тестирование, то следующий участок кода стратегии:
Code

... lock (this)
	{
	  if (_bidOrder == null && _askOrder == null)
		{
		// проверяем на сигнал на вход
	       var spread = (ask - bid) / Security.MinStepSize;

		if (spread >= ((RobotStrategyProperties)Params).Spread)
...

вызовет ошибку "Не удалось привести тип объекта "System.Collections.Generic.List`1[Robot.Strategies.HustleEveryDayStrategyTestingProperties]" к типу "System.Collections.Generic.List`1[Robot.Strategies.HustleEveryDayStrategyProperties]".

Для решения этой проблемы введем в класс BaseShellStrategyProperties два параметра "Класс стратегии" и "Режим запуска":
Code


        public enum enMode
        {
           Execution,
           Testing
        }

        private string _classname = "Класс стратегии";
        [DisplayName(@"Класс")]
        [Description(@"Класс стратегии")]
        [Category(@"Основные")]
        [PropertyOrder(1)]
        public string ClassName
        {
            get { return _classname; }
            set
            {
                _classname = value;
                OnPropertyChanged("ClassName");
            }
        }


        private enMode _mode = enMode.Testing;
        [Category(@"Основные")]
        [DisplayName(@"Режим запуска")]
        [Description(@"Режим запуска стратегии")]
        [PropertyOrder(2)]
        public enMode Mode
        {
            get { return _mode; }
            set
            {
                _mode = value;
                OnPropertyChanged("Mode");
            }
        }


Данные свойства необходимо определять во всех процедурах типа AddХХХХStrategy.
Code

     private void AddRobotStrategy()
        {
            var properties = new RobotStrategyProperties
            {
                ClassName = "Robot",
                Mode = enMode.Execution



4.1. Универсализация исполнения стратегий
Все параметры внутри стратегии обертываем процедурой if, которая проверяет режим запуска и проводит правильное приведение типов данных:
Code

            int StopVal = 1;
            if (Params.Mode == enMode.Execution)
                StopVal = ((RobotStrategyProperties)Params).Stop;
            else
                StopVal = ((RobotTestingProperties)Params).Stop;



4.2. Универсализация тестирования стратегий
В основную процедуру запуска стратегий на тестирование StartTesingStrategy добавляем проверка класса стратегии при задании специфичных параметров:
Code

            if (parameters.ClassName == "Sma" || parameters.ClassName == "TLS")
            {
                switch (parameters.ClassName)
                {
                    case  "Sma":
                        ((SmaStrategy)_strategy).series = _series;
                        break;
                    case "TLS":
                        ((TLSStrategy)_strategy).series = _series;
                        break;
                }
            }



4.3. Универсализация сохранения настроек стратегий
В основной процедуре сохранения настроек стратегии SaveStrategies проводим модификацию запроса Where
Code

var robotstrategies = _documents.Keys.Where(str => str.Params.ClassName == "Robot" && str.Params.Mode == enMode.Execution).Select(str => (RobotStrategyProperties)str.Params).ToList();
SettingsEngine.Instance.SaveRobotStrategies(robotstrategies);

var hustlestrategies = _documents.Keys.Where(str => str.Params.ClassName == "Hustle" && str.Params.Mode == enMode.Execution).Select(str => (HustleStrategyProperties)str.Params).ToList();
SettingsEngine.Instance.SaveHustleStrProp(hustlestrategies);



4.4. Устранение бага отображения стратегий на панели "Стратегии"
В поставляемой версии на панели "Стратегии" не отражаются тестовые стратегии. Для устранения этого бага проводим универсализацию хранения стратегий в переменной _documents.
а) везде в процедурах создания стратегий проводим сохранение стратегий в переменную _documents:
Code

                _documents.Add(strategy, doc); 

б) в свойстве SelectedStrategy дописываем условие выбора документа типа TestingDocument:
Code

                var doc = dockManager.ActiveContent;

                if (doc is StrategyDocument)
                {
                    var content = (StrategyDocument)doc;
                    result = content.Strategy;
                }
                else
                {
                    var content = (TestingDocument)doc;
                    result = content.Strategy;
                }
                return result;

Теперь все отображается корректно.
в) дополнительно можно вообще удалить процедуру SaveTestingStrategies, поместив соответствующие обработки в SaveStrategies:
Code

var robotTstrategies = _documents.Keys.Where(str => str.Params.ClassName == "Robot" && str.Params.Mode == enMode.Testing).Select(str => (RobotStrategyTestingProperties)str.Params).ToList();
SettingsEngine.Instance.SaveRobotTestingStrategies(robotTstrategies);

var hustleTstrategies = _documents.Keys.Where(str => str.Params.ClassName == "Hustle" && str.Params.Mode == enMode.Testing).Select(str => (HustleStrategyTestingProperties)str.Params).ToList();
SettingsEngine.Instance.SaveHustleTestStrProp(hustleTstrategies);



После всех этих универсализаций добавление новой стратегии действительно становится достаточно простым делом.
StrategyPane.jpg 253 KB (224)
Thanks:

JaguarFX

Avatar
Date: 2/16/2014
Reply


Часть 5. Чему нас учит семья и школа

Интерфейс тестирования S#.Shell по умолчанию не достаточен для эффективного тестирования стратегии при ее первичном кодировании, так как в этом тонком процессе переноса собственного нейронного вещества в машинный код возникает множество нюансов а-ля "сделка не выполнилась в силу неизвестных причин", в которых невозможно разобраться без соответствующих инструментов.

Поэтому добавляем в наш S.Shell основные инструменты визуализации процесса исполнения стратегии, которые нам уже знакомы по курсу S#.Education - исторический график свечей, таблицу заявок и таблицу собственных сделок.

5.1. Создаем на TestingPanel элемент типа TabControl, в который добавляем вкладки для каждого инструмента:
Code

                <TabItem x:Name="TabHist" Header="History">
                    <chart:Chart x:Name="HistoryChart" MinHeight="200" Height="Auto" x:FieldModifier="public"/>
                </TabItem>
                <TabItem x:Name="TabPNL" Header="PNL">
                    <chart:EquityCurveChart x:Name="CurveChart" MinHeight="200" Height="Auto"  x:FieldModifier="public"/>
                </TabItem>
                <TabItem x:Name="TabORD" Header="Orders">
                    <xaml:OrderGrid x:Name="myOrders" MinHeight="200" Height="Auto" x:FieldModifier="public"/>
                </TabItem>
                <TabItem x:Name="TabTRD" Header="Trades">
                    <xaml:MyTradeGrid x:Name="myTrades" MinHeight="200" Height="Auto" x:FieldModifier="public"/>
                </TabItem>


5.2. В общем-то мы понимаем, что графические элементы в целом тормозят тестирование и нужны будут не всегда, а в основном при кодинге новых стратегий, поэтому в свойства базовой стратегии тестирования BaseShellStrategy добваляем свойства для управления отображением их в ходе тестирования:
Code

        private bool _chartPL = false;
        [Category(@"Отрисовка")]
        [DisplayName(@"График P&L")]
        [Description(@"Вывод графика P&L при тестировании")]
        [PropertyOrder(0)]
        public bool chartPL
        {
            get { return _chartPL; }
            set
            {
                _chartPL = value;
                OnPropertyChanged("chartPL");
            }
        }

        private bool _chartHC = false;
        [Category(@"Отрисовка")]
        [DisplayName(@"График свечей")]
        [Description(@"Вывод графика свечей  при тестировании")]
        [PropertyOrder(2)]
        public bool chartHC
        {
            get { return _chartHC; }
            set
            {
                _chartHC = value;
                OnPropertyChanged("chartHC");
            }
        }

        private bool _chartTrd = false;
        [Category(@"Отрисовка")]
        [DisplayName(@"Сделки")]
        [Description(@"Вывод всех сделок на график истории")]
        [PropertyOrder(2)]
        public bool chartTrd
        {
            get { return _chartTrd; }
            set
            {
                _chartTrd = value;
                OnPropertyChanged("chartTrd");
            }
        }



5.3. В процедуре тестирования стратегии StartTesingStrategy прописываем код для обоработки каждого нового инструмента:
а) для истории свечей:
Code

            if (parameters.chartHC)
            {
                    histChart = doc.TestingPanel.HistoryChart;
                    histChart.Areas.Clear();
                    histChart.IsAutoScroll = true;
                    _area = new ChartArea(); //создаем область на графике
                    histChart.Areas.Add(_area); //добавляем область на график
                    _candlesElem = new ChartCandleElement(); //создаем элемент свечи
                    _area.Elements.Add(_candlesElem); //добавляем элемент в область вывода графика
                    _candleManager.Processing += (series, cnd) =>
                    {
                        if (cnd.State == CandleStates.Finished)
                        {
                            var myDic = new Dictionary<IChartElement, object>();
                            myDic.Add(_candlesElem, cnd);
                            MainWindow.Instance.GuiAsync(() => histChart.ProcessValues(cnd.OpenTime, myDic));
                        }
                    };
            }

б) для отражения истории сделок:
Code

            if (parameters.chartHC && parameters.chartTrd)
            {
                    var _tradeElement = new ChartTradeElement();
                    _area.Elements.Add(_tradeElement);
                    _strategy.NewMyTrades += (trds) => 
                        trds.ForEach(t =>
                        {
                            var tradeTime = _timeFrame.GetCandleBounds(t.Trade.Time).Min;
                            var myDic = new Dictionary<IChartElement, object>() { { _tradeElement, t } };
                            MainWindow.Instance.GuiAsync(() => histChart.ProcessValues(tradeTime, myDic));
                        });
            }

в) для таблицы заявок:
Code

            var _myOrders = doc.TestingPanel.myOrders;
            _myOrders.Orders.Clear();
            _strategy.OrderRegistered += (ord) => MainWindow.Instance.GuiAsync(() => _myOrders.Orders.Add(ord));

г) для таблицы сделок:
Code

            var _myTrades = doc.TestingPanel.myTrades;
            _myTrades.Trades.Clear();
            _strategy.NewMyTrades += (trd) => trd.ForEach(t => MainWindow.Instance.GuiAsync(() => _myTrades.Trades.Add(t)));


И в результате все работет отлично, кроме корректного отображения сделок))
History.jpg 529 KB (224) Orders.jpg 710 KB (239) MyTrades.jpg 430 KB (231)
Thanks:

kesot

Avatar
Date: 2/20/2014
Reply


А почему для установки стопов не пользуетесь StopLossStrategy, а в ручную их отслеживаете?
Thanks:

JaguarFX

Avatar
Date: 2/24/2014
Reply


на мой взгляд использование дочерних стратегий тут дело вкуса - кому-то идет, а кому-то нет; в силу разных обстоятельств - и дебаггить их невозможно, условия срабатывания ограничены (н--р нет стоп-лосса исходя из % годовых) и пр.
Thanks:

JaguarFX

Avatar
Date: 3/1/2014
Reply


Часть 6 - Тотальный апгрейд

Итак имеем S#.Shell на основе S#.API 4.1.15.0 и решаемся на апгрейд до версии 4.2.2.16.
Сразу же получаем множество заменой iTrader на Connector, и еще с десяток проблемных мелочей типа замены свойств, которые решаются в течение получаса

Но так же вылезают две более серьезные проблемы, о которых имеет смысл тут рассказать.

1. В XAML отваливается весь CommandBinding с тупым сообщением "Член не распознан"))
Как единственное найденное 100% работающее решение данной проблемы - перенос всего CommandBindingа из XAML в программный код.
Для этого делаем три простых шага:
1) в MainWindow.xaml полностью удаляем содержание раздела <Window.CommandBindings> и привязки команд типа Command="{x:Static Robot:MainWindow.ConnectCommand}", а вместо этого для каждого элемента, которому необходимо привязать RoutedCommand задаем имя через тег x:Name
2) в MainWindow.xaml.cs создаем новую процедуру private void InitializeCommands(), в которой для каждого управляющего элемента создаем связь через класс CommandBinding (на примере пункта меню "Подключиться"):
Code

            CommandBinding ccBinding1 = new CommandBinding(ConnectCommand, ExecutedConnect, CanExecuteConnect);
            this.CommandBindings.Add(ccBinding1);
            miConnect.Command = ConnectCommand;

где ConnectCommand - исходная RoutedCommand,
ExecutedConnect - основная процедура для выполнения,
CanExecuteConnect - связанная проверочная процедура возможности выполнения основной процедуры,
miConnect - название элемента пункта меню "Подключиться",
далее в тоже процедуре InitializeCommands() создаем необходимые элементы для быстрых клавиш:
Code

            KeyGesture OpenKeyGesture1 = new KeyGesture(Key.P,ModifierKeys.Control);
            InputBinding KeyBinding1 = new KeyBinding(SettingsCommand, OpenKeyGesture1);
            this.InputBindings.Add(KeyBinding1);

3) вызываем InitializeCommands() в теле процедуры MainWindow().

2. В S#.API версии 4.2.2.16 обнаруживаем, что в режиме эмуляции ни один из коннекторов (проверено на AlfaTrader/TransaqTrader) не получает портфели. Соответственно тестирование в эмуляции невозможно.
Для решения этой проблемы делаем два шага:
1) создаем в классе ConnectionEngine свойство IsEmulation:
Code

                   public bool IsEmulation { get { return settings.Emulation; } }

2)добавляем в код процедуры StartStrategy специальную проверку на режим эмуляции и в случае оного, создаем тестовый портфель, который и привязываем к коннектору:
Code
 
		    var portfolio = ConnectionEngine.Instance.Trader.Portfolios.FirstOrDefault(p => p.Name == strategy.Params.Portfolio);
		    if (portfolio == null)
		    {
		        if (ConnectionEngine.Instance.IsEmulation)
		        {
                    var portf = new Portfolio{Name = "TestAcc",BeginValue = 1000000,CurrentValue = 1000000};
                    ConnectionEngine.Instance.Trader.RegisterPortfolio(portf);
                    ConnectionEngine.Instance.Trader.TransactionAdapter.SendInMessage(portf.ToMessage());
                    var pcm =new PortfolioChangeMessage{PortfolioName = portf.Name}.Add(PositionChangeTypes.BeginValue, portf.BeginValue);
		            ConnectionEngine.Instance.Trader.TransactionAdapter.SendInMessage(pcm);
                    var pcm2 = new PortfolioChangeMessage { PortfolioName = portf.Name }.Add(PositionChangeTypes.CurrentValue, portf.CurrentValue);
                    ConnectionEngine.Instance.Trader.TransactionAdapter.SendInMessage(pcm2);
		            strategy.Portfolio = portf;
                    strategy.AddWarningLog("Создан портфель {0} для запуска стратегии", portf.Name);
		        }
		        else
		        {
                    strategy.AddErrorLog("Не найден портфель {0} для запуска стратегии", strategy.Params.Portfolio);
                    return;    
		        }
		        
		    }
		    else
		        strategy.Portfolio = portfolio;


Вуаля- все работает как нужно!
Thanks: methyst

methyst

Avatar
Date: 3/10/2014
Reply


lebedevsrg, собираетесь ли Вы выкладывать свою версию shell'a куда-нибудь?
Thanks:

JaguarFX

Avatar
Date: 3/10/2014
Reply


methyst, тут такие правила что по окончанию срока обучения (1,5 или 3 мес ) пользователей отключают от TFS.
По вопросу - полагаю что смысла каждому пользователю выкладывать свои программы на TFS особого нет.
Материалы я тут опубликовал для общего развития тех кто захочет сам допиливать Shell.
Thanks:
< 1 2 3  >

Attach files by dragging & dropping, , or pasting from the clipboard.

loading
clippy