← Zurück

Закрыть все позиции с котированием

Пробую реализовывать простые сценарии на S#. Один из таких сценариев - закрытие всех позиций через стратегию котирования (какую именно - не принципиально, пусть будет LimitQuotingStrategy). По этому поводу написано нехитрое тестовое WPF приложение (текст приведен ниже).

Проблема заключается в том, что если раскомментировать строки 314-326 и 329 (т.е. создание стратегии для каждого инструмента и её запуск), то в обработчике события NewMarketDepths вылетает исключение:

An exception of type 'System.NullReferenceException' occurred in WPF stocksharp study.exe but was not handled in user code

Additional information: Ссылка на объект не указывает на экземпляр объекта.

If there is a handler for this exception, the program may be safely continued.

Экспериментально установил, что исключение связано с тем, что идет обращение к d.BestBid.Price (т.е. получение последней лучшей котировки).

Если оставить в приведенном коде всё как есть с комментариями, то стаканы для инструментов, по которым есть открытые позиции, стартуют отлично и в текстовом поле LogWindow цена лучшего бида появляется - т.е. обработчик события NewMarketDepths не падает из-за пустого указателя.

Что я делаю не так? Очевидно, я не учел какую-то очень важную особенность при работе с библиотекой. Но какую???

Версия библиотеки - 4.1.6.

PS: Если кто-то найдет что-либо полезное в моем примере - не возбраняется забрать на заметку с целью дальнейшего использования. :)

Код MainWindow.xaml:

<?xml version="1.0" encoding="utf-8"?>
<Window
	x:Class="WPF_stocksharp_study.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
    xmlns:Xaml="clr-namespace:StockSharp.Xaml;assembly=StockSharp.Xaml"
	Title="WPF_stocksharp_study"
    x:Name="mainWindow"
	ResizeMode="CanMinimize" Width="500" Height="520"
>
    <StackPanel>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="5">
            <Label Content="Путь к QUIK:" HorizontalAlignment="Left" VerticalAlignment="Center" />
            <TextBox Name="Path" HorizontalAlignment="Left" VerticalAlignment="Center" Width="350" />
            <Button Name="LocateQuikButton" Content="..." HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="35" Margin="10,0,10,0" IsEnabled="True" Click="LocateQuikButton_Click" IsDefault="True" />
        </StackPanel>
        <StackPanel Margin="5">
            <StackPanel Orientation="Horizontal">
                <Button Name="ConnectButton" Content="Подключиться" Click="ConnectButton_Click" />
                <Button	Name="StartWatchSec1Sec2Button" Content="Включить экспорт инструментов" Click="StartWatchSec1Sec2Button_Click" IsEnabled="False" />
                <Button Name="ShowDepthsForPositionsButton" Content="Экспорт инструментов в позициях" Click="ShowDepthsForPositionsButton_Click" IsEnabled="False" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">

            </StackPanel>
        </StackPanel>
        <Grid Margin="5" HorizontalAlignment="Left">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="100"/>
            </Grid.ColumnDefinitions>
            <Label Content="Инструмент 1" Grid.Column="0" Grid.Row="0" />
            <Label Content="Инструмент 2" Grid.Column="0" Grid.Row="1" />
            <ComboBox x:Name="Security1List" SelectionChanged="SecurityList_SelectionChanged" HorizontalAlignment="Stretch" Grid.Column="1" Grid.Row="0" IsEnabled="False" />
            <ComboBox x:Name="Security2List" SelectionChanged="SecurityList_SelectionChanged" HorizontalAlignment="Stretch" Grid.Column="1" Grid.Row="1" IsEnabled="False" />
        </Grid>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="5">
            <Label Content="Позиции:" />
            <ListView x:Name="MyPositions" Height="150" ItemsSource="{Binding}" FontSize="10" >
                <ListView.View>
                    <GridView>
                        <GridViewColumn Width="100" Header="Счет" DisplayMemberBinding="{Binding Path=Portfolio.Name}" />
                        <GridViewColumn Width="100" Header="Инструмент" DisplayMemberBinding="{Binding Path=Security.Code}" />
                        <GridViewColumn Width="100" Header="Позиция" DisplayMemberBinding="{Binding Path=CurrentValue}" />
                        <GridViewColumn Width="100" Header="Заблокировано" DisplayMemberBinding="{Binding Path=BlockedValue}" />
                    </GridView>
                </ListView.View>
            </ListView>
        </StackPanel>
        <StackPanel>
            <TextBox x:Name="LogWindow" IsReadOnly="True" VerticalScrollBarVisibility="Auto" VerticalContentAlignment="Top" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Height="166" TextWrapping="Wrap" />
        </StackPanel>
    </StackPanel>
</Window>

Код MainWindow.xaml.cs

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Ecng.Xaml;
using Ecng.ComponentModel;
using Ecng.Collections;
using Ecng.Common;
using System.Linq;
using StockSharp.Algo.Strategies;
using StockSharp.Algo;
using StockSharp.Logging;
using StockSharp.Xaml;
using StockSharp.BusinessEntities;
using StockSharp.Quik;
using MessageBox = System.Windows.MessageBox;


namespace WPF_stocksharp_study
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public QuikTrader _trader;
        public ThreadSafeObservableCollection<Position> Positions = new ThreadSafeObservableCollection<Position>();
        public static MainWindow Instance { get; private set; }

        private bool _isConnected = false, _isDdeStarted = false, _isWatchingStarted = false, _isQuotesRegistered = false, _isPositionsRegistered = false;
        private Security sec1, sec2;
        private List<Strategy> _strategies = new List<Strategy>();

        private LogManager LogManager = new LogManager();

        public MainWindow()
        {
            InitializeComponent();

            MainWindow.Instance = this;
            this.DataContext = this;

            this.Path.Text = QuikTerminal.GetDefaultPath();
            MyPositions.DataContext = Positions;

            var consoleLogListener = new ConsoleLogListener();
            this.LogManager.Listeners.Add(consoleLogListener);
        }

        #region Routines
        void StartDDE()
        {
            WriteLogMessage("Запускается экспорт DDE");

            _trader.StartExport(new[]
			                    {
			                    	_trader.SecuritiesTable,
			                    	_trader.EquityPositionsTable,
			                    	_trader.EquityPortfoliosTable,
                                    _trader.OrdersTable,
                                    _trader.TradesTable,
                                    _trader.MyTradesTable
			                    });

            UpdateControls();
        }

        void StopDDE()
        {
            WriteLogMessage("Останавливается экспорт DDE");
            _trader.StopExport();
            UpdateControls();
        }

        void StartWatch()
        {
            this._isWatchingStarted = true;

            this.sec1 = (Security)Security1List.SelectedItem;
            this.sec2 = (Security)Security2List.SelectedItem;

            if (sec1 != null)
                this._trader.RegisterMarketDepth(this.sec1);
            if (sec2 != null)
                this._trader.RegisterMarketDepth(this.sec2);
            _isQuotesRegistered = true;

            UpdateControls();
        }

        void StopWatch()
        {
            if (_isQuotesRegistered)
            {
                this._trader.UnRegisterMarketDepth(this.sec1);
                this._trader.UnRegisterMarketDepth(this.sec2);
            }

            this._isWatchingStarted = false;
            UpdateControls();
        }

        void UpdateControls()
        {
            ConnectButton.Content = (_isConnected) ? "Отключить" : "Подключить";
            _isDdeStarted = _trader.IsExportStarted;
            StartWatchSec1Sec2Button.Content = (_isDdeStarted) ? "Выключить экспорт инструментов" : "Включить экспорт инструментов";
            StartWatchSec1Sec2Button.IsEnabled = (Security1List.SelectedItem != null) && (Security2List.SelectedItem != null);
            Security1List.IsEnabled = Security2List.IsEnabled = _isDdeStarted;
            ShowDepthsForPositionsButton.IsEnabled = _isPositionsRegistered;
        }


        void WriteLogMessage(string message)
        {
            var _dt = DateTime.Now;
            var str = _dt.ToString("yyyy-MM-dd HH:mm:ss.ffff - ") + message + "\n";

            LogWindow.AppendText(str);
            LogWindow.ScrollToEnd();
        }

        void CalculateAndUpdateBidAsk()
        {
            // TODO: Implement CalculateAndUpdateBidAsk
        }
        #endregion

        #region Event handlers
        #region Event handlers for UI elements
        void ConnectButton_Click(object sender, RoutedEventArgs e)
        {
            if (!_isConnected)
            {
                if (this.Path.Text.IsEmpty())
                    MessageBox.Show(this, "Путь к Quik не выбран");
                else
                {
                    WriteLogMessage("Начинаем подключение!");
                    if (this._trader == null)
                    {
                        this._trader = new QuikTrader(this.Path.Text);
                        this.LogManager.Sources.Add(_trader);

                        // Подписываемся на событие появления портфелей
                        this._trader.NewPortfolios += portfolios => this.GuiAsync(() =>
                                                                                  {
                                                                                      WriteLogMessage("Портфели появились!");
                                                                                  });

                        this._trader.PortfoliosChanged += portfolios => this.GuiAsync(() =>
                                                                                   {
                                                                                       WriteLogMessage("Портфели изменились!");
                                                                                       portfolios.ForEach(p => WriteLogMessage(p.Name));
                                                                                   });

                        // Подписываемся на событие появления инструментов
                        this._trader.NewSecurities += securities => this.GuiAsync(() =>
                                                                                  {
                                                                                      Security1List.ItemsSource = this._trader.Securities;
                                                                                      Security2List.ItemsSource = this._trader.Securities;

                                                                                      WriteLogMessage("Инструменты появились!");
                                                                                  });

                        this._trader.NewMarketDepths += depths => this.GuiAsync(() =>
                                                                                {
                                                                                    depths.ForEach(d =>
                                                                                    {
                                                                                        WriteLogMessage(string.Format("Появился стакан для инструмента {0}. Бид: {1}", d.Security.Code, d.BestBid.Price));
                                                                                    });
                                                                                });

                        // Подписываемся на событие появления моих сделок
                        this._trader.NewMyTrades += myTrades => this.GuiAsync(() =>
                                                                              {
                                                                                  foreach (var myTrade in myTrades)
                                                                                  {
                                                                                      var trade = myTrade.Trade;
                                                                                      WriteLogMessage(string.Format("Сделка {0} по цене {1} по бумаге {2} по объему {3} в {4}.", trade.Id, trade.Price, trade.Security.Code, trade.Volume, trade.Time));
                                                                                  }
                                                                              });

                        // Подписываемся на событие подлючения
                        this._trader.Connected += () => this.GuiAsync(() =>
                                                                      {
                                                                          _isConnected = true;
                                                                          WriteLogMessage("QUIK подключен!");
                                                                          StartDDE();
                                                                          UpdateControls();
                                                                      });

                        //this._trader.NewPositions += positions => this.GuiAsync(() =>
                        //                                                {
                        //                                                    Positions.Clear();
                        //                                                    Positions.AddRange(_trader.Positions.Where(p => p.CurrentValue != 0));
                        //                                                });

                        this._trader.PositionsChanged += positions => this.GuiAsync(() =>
                                                                      {
                                                                          Positions.Clear();
                                                                          Positions.AddRange(_trader.Positions.Where(p => p.CurrentValue != 0));

                                                                          if (!_isPositionsRegistered)
                                                                          {
                                                                              _isPositionsRegistered = true;
                                                                              UpdateControls();
                                                                          }
                                                                      });

                        // Подписываемся на событие отключения
                        this._trader.Disconnected += () => this.GuiAsync(() =>
                                                                         {
                                                                             _isConnected = false;
                                                                             WriteLogMessage("QUIK отключен!");
                                                                             UpdateControls();
                                                                         });

                        this._trader.ConnectionError += (f) => this.GuiAsync(() =>
                                                                        {
                                                                            WriteLogMessage("Ошибка подключения. " + f.Message);
                                                                        });

                    }
                    // Подключаемся
                    this._trader.Connect();
                }
            }
            else
            {
                StopWatch();
                StopDDE();
                this._trader.Disconnect();

                _isConnected = false;
                UpdateControls();
            }
        }

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

            if (!this.Path.Text.IsEmpty())
                dlg.SelectedPath = this.Path.Text;

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


        void StartWatchSec1Sec2Button_Click(object sender, RoutedEventArgs e)
        {
            if (_isWatchingStarted)
                StopWatch();
            else
                StartWatch();
        }

        void SecurityList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            sec1 = (Security)Security1List.SelectedItem;
            sec2 = (Security)Security2List.SelectedItem;

            UpdateControls();
        }
        #endregion

        #region Event handlers for application shutdown
        protected override void OnClosing(CancelEventArgs e)
        {
            if (this._trader != null)
            {
                if (_isDdeStarted)
                    StopDDE();
                if (_isWatchingStarted)
                    StopWatch();

                this._trader.Dispose();
            }

            base.OnClosing(e);
        }

        protected override void OnClosed(EventArgs e)
        {
            System.Windows.Application.Current.Shutdown();
            base.OnClosed(e);
        }
        #endregion

        private void ShowDepthsForPositionsButton_Click(object sender, RoutedEventArgs e)
        {
            _trader.Positions.ForEach(p =>
                {
                    WriteLogMessage("Актив: " + p.Security.Code + " Позиция: " + p.CurrentValue);

                    var _volume = p.CurrentValue;

                    if (_volume != 0)
                    {
                        WriteLogMessage("Запускается экспорт стакана для" + p.Security.Code);
                        _trader.RegisterMarketDepth(p.Security);
                    }

                    //var _depth = _trader.GetMarketDepth(p.Security).Clone();

                    //var _close = new LimitQuotingStrategy(_volume > 0 ? OrderDirections.Sell : OrderDirections.Buy, _volume.Abs(), (_depth.BestAsk.Price + _depth.BestBid.Price) / 2)
                    // {
                    //     Trader = this._trader,
                    //     Security = p.Security,
                    //     Portfolio = p.Portfolio
                    // };

                    //this.LogManager.Sources.Add(_close);

                    //_strategies.Add(_close);
                });

            //_strategies.ForEach(s => s.Start());
        }
        #endregion
    }
}

War dieses Thema hilfreich?

Thema teilen

Kommentare (8)

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

Algonavt
Algonavt Autor

Проблема возникает из-за того, что стакан не успевает запуститься к моменту обращения к BestBid в коде создания стратегии. Для эксперимента сделал две кнопки - одну на запуск стаканов с котировками инструментов, по которым открыты позиции, вторую - на закрытие самих позиций. Всё работает тип-топ, при наличии уже подгруженных стаканов позиции закрываются на "ура".

Стало быть, перед тем, как создавать страегии котирования (или выставлять заявки), надо как-то дождаться прихода события о появлении стакана для всех инструментов. Как это сделать - почти придумал, если кому интересно - выложу по выполнении.

Велосипед, однако. :)

vil
vil

Прошу не бросать помидорами. Из меня программист вообще никакой. Но позволю спросить, а что мешает использовать "Семафоры" для временной паузы перед регистрацией заявки или еще чем то до момента прихода события получения стакана ?

VassilSanych
VassilSanych

vil: Прошу не бросать помидорами. Из меня программист вообще никакой. Но позволю спросить, а что мешает использовать "Семафоры" для временной паузы перед регистрацией заявки или еще чем то до момента прихода события получения стакана ? Ничего не мешает. Но проще просто подписаться на событие. Чем короче методы, тем лучше.

Algonavt
Algonavt Autor

VassilSanych:

vil: Прошу не бросать помидорами. Из меня программист вообще никакой. Но позволю спросить, а что мешает использовать "Семафоры" для временной паузы перед регистрацией заявки или еще чем то до момента прихода события получения стакана ? Ничего не мешает. Но проще просто подписаться на событие. Чем короче методы, тем лучше.

Собственно, идея состояла в том, чтобы к моменту, когда потребуется регистрировать заявки по инструменту, стакан уже был запущен. Тогда и ожидать ничего не придется. Запуск стакана - как правило разовая операция (при старте или перезагрузке робота), работа с заявками - многократная. Так что перед каждой заявкой проверять/стартовать стакан - некруто с точки зрения производительности, как я думаю.

Игорь Бакулин
Игорь Бакулин

Algonavt: //var _depth = _trader.GetMarketDepth(p.Security).Clone();

                //var _close = new LimitQuotingStrategy(_volume > 0 ? OrderDirections.Sell : OrderDirections.Buy, _volume.Abs(), (_depth.BestAsk.Price + _depth.BestBid.Price) / 2)
                // {
                //     Trader = this._trader,
                //     Security = p.Security,
                //     Portfolio = p.Portfolio
                // };

Попробуйте так.



                    var _depth = _trader.GetMarketDepth(p.Security).Clone();

                    if (_depth != null)
                    {
                    var _close = new LimitQuotingStrategy(_volume > 0 ? OrderDirections.Sell : OrderDirections.Buy, _volume.Abs(), (_depth.BestAsk.Price + _depth.BestBid.Price) / 2)
                     {
                         Trader = this._trader,
                         Security = p.Security,
                         Portfolio = p.Portfolio
                     };
                     }


еще бывает полезно проверять на not null _depth.BestAsk и _depth.BestBid т.к. для получения этих данных системе требуется какое-то время. А пока данные не подгружены эти значения равны null, т.е. не инициализированы.

Ну а если вам важно отрабатывать нажатие на кнопку ShowDepthsForPositionsButton когда уже точно стакан подгружен, то можно так в xaml пишем ShowDepthsForPositionsButton.IsEnabled = false т.е. делаем кнопку не активной. а в строки 169-175 добавить ShowDepthsForPositionsButton.IsEnabled = true;



this._trader.NewMarketDepths += depths => this.GuiAsync(() =>
     {
       depths.ForEach(d =>
           {		    
               WriteLogMessage (string.Format ( "Появился стакан для инструмента {0}. Бид: {1}", d.Security.Code, d.BestBid.Price));
           });
       ShowDepthsForPositionsButton.IsEnabled = true;
     });

Algonavt
Algonavt Autor

Почти так и сделал. Реализовал такой код:

public class DepthStarter
    {
        public bool WaitForMarketDepth(ITrader trader, Security security)
        {
            if (security == null)
                throw new ApplicationException("DepthSelector: security == null!");

            var _depthTrigger = false;

            ManualResetEventSlim manualResetEvent = new ManualResetEventSlim(false);
            Action<IEnumerable<MarketDepth>> onDepthsChanged = p =>
            {
                if (FindDepth(trader, security))
                    manualResetEvent.Set();
            };

            try
            {
                trader.NewMarketDepths += onDepthsChanged;
                trader.MarketDepthsChanged += onDepthsChanged;

                trader.RegisterMarketDepth(security);

                _depthTrigger = FindDepth(trader, security);
                if (!_depthTrigger)
                    _depthTrigger = manualResetEvent.Wait(TimeSpan.FromSeconds(10));

                return _depthTrigger;

                //return FindDepth(trader, security) || manualResetEvent.Wait(TimeSpan.FromSeconds(10));   // Это НЕ РАБОТАЕТ!! Оставлено, чтобы помнить, как делать не надо.
            }
            finally
            {
                trader.NewMarketDepths -= onDepthsChanged;
                trader.MarketDepthsChanged -= onDepthsChanged;
            }
        }

        private bool FindDepth(ITrader trader, Security security)
        {
            return trader.RegisteredMarketDepths.Contains(security) ? (security.BestBid != null) || (security.BestAsk != null) : false;
        }
    }
Algonavt
Algonavt Autor

Собственно, я перед тем, как создавать дочерние стратегии распродажи портфеля запускаю стаканы для всех инструментов. Дочерние стратегии я начинаю создавать только если все стаканы запустились. То есть, для каждого из инструментов, по которому есть ненулевая позиция, сперва создается стакан через метод DepthStarter.WaitForMarketDepth(ITrader trader, Security security).

По поводу проверки _depth.BestAsk и _depth.BestBid на != null - согласен, в "боевом" режиме это надо делать, т.к. в теории в стакане могут исчезнуть все биды или все аски (для наиболее ликвидных инструментов это, конечно, маловероятно, но тем не менее лучше страховаться) - в документации на стокшарп такой тип "экстремальных" случаев также упоминается как возможный и, след-но, подлежащий обработке. Просто в примере не стал его включать.

А вот по поводу "для получения этих данных системе требуется какое-то время" вопрос на самом деле очень интересный. Тут я хочу спросить у разработчиков библиотеки, как задумана в логике библиотеки обработка ситуаций, когда стакан запущен, но новые данные по стакану из трейдера ещё не прибежали. Полагаю, что в качестве BestPair (и, соответственно, BestBid и BestAsk) предоставляется последнее закешированное значение. Так ли это?

VassilSanych
VassilSanych

Какое-то странное поведение Если подписаться на лог стратегии котирования

2013/02/04 17:37:43.749|       |LQS_RIH3@RTS_SPBFUT00066|Стратегия запущена. [0,-1]. Позиция при старте 0.
2013/02/04 17:37:43.752|       |LQS_RIH3@RTS_SPBFUT00066|Котирование на Sell объема 1.
2013/02/04 17:37:43.753|       |LQS_RIH3@RTS_SPBFUT00066|Приостановка правил. _rulesSuspendCount 1.
2013/02/04 17:37:43.771|       |LQS_RIH3@RTS_SPBFUT00066|Возобновление правил. _rulesSuspendCount 0.
2013/02/04 17:37:43.779|       |LQS_RIH3@RTS_SPBFUT00066|Цена текущей NULL и лучшей 161120.
2013/02/04 17:37:43.779|       |LQS_RIH3@RTS_SPBFUT00066|Лучший бид 161110 и лучший аск 161130.
2013/02/04 17:37:43.786|       |LQS_RIH3@RTS_SPBFUT00066|Регистрация новой Limit (0x159ADC6) заявки на Sell с ценой 161120 и объемом 1. 
2013/02/04 17:37:44.121|Warning|LQS_RIH3@RTS_SPBFUT00066|Заявка 63437501 в процессе регистрации.
2013/02/04 17:37:44.123|       |LQS_RIH3@RTS_SPBFUT00066|Заявка 63437501 принята биржей.
2013/02/04 17:37:44.124|       |LQS_RIH3@RTS_SPBFUT00066|Сброс счетчика ошибок регистрации с 0 до нуля.
2013/02/04 17:37:44.127|       |QuikManager|316 Заявка 63437501/1981856709 Продажа Цена=161120 Объем=1 Сост=Active Бал=1
2013/02/04 17:37:48.768|       |QuikManager|768 Сделка 04.02.2013 17:37:48 82247758 161120 1 от заявки 63437501/1981856709 Продажа Цена=161120 Объем=1 Сост=Done Бал=0
2013/02/04 17:37:48.771|       |LQS_RIH3@RTS_SPBFUT00066|Новая Sell сделка 82247758 по цене 161120 на 1 заявки 63437501.
2013/02/04 17:37:48.770|       |QuikManager|316 Заявка 63437501/1981856709 Продажа Цена=161120 Объем=1 Сост=Done Бал=0
2013/02/04 17:37:48.772|       |LQS_RIH3@RTS_SPBFUT00066|Новая позиция: SPBFUT00066-RIH3@RTS=-1.
2013/02/04 17:37:48.773|       |LQS_RIH3@RTS_SPBFUT00066|Позиция изменилась на -1. Оставшийся объем 0.
2013/02/04 17:37:48.773|       |LQS_RIH3@RTS_SPBFUT00066|Заканчиваем котирование.
2013/02/04 17:37:48.773|       |LQS_RIH3@RTS_SPBFUT00066|Стратегия останавливается. [0,-1]. Позиция при старте -1.
2013/02/04 17:37:48.775|       |LQS_RIH3@RTS_SPBFUT00066|Правило 'Изменение стакана инструмента RIH3@RTS (0x2790E95)'. Приостановлено.
2013/02/04 17:37:48.775|       |LQS_RIH3@RTS_SPBFUT00066|Ожидание снятия всех активных заявок.
2013/02/04 17:37:48.780|       |LQS_RIH3@RTS_SPBFUT00066|Стратегия остановлена. [0,-1]. Позиция при старте -1.

Вроде нормально (QuikManager - мой класс, который ловит события шлюза). А вот если не подписываться:

2013/02/04 17:25:05.735|       |QuikManager|196 Заявка 62514036/1981813976 Покупка Цена=161230 Объем=1 Сост=Done Бал=1
2013/02/04 17:25:05.970|       |QuikManager|196 Заявка 62514036/1981813976 Покупка Цена=161230 Объем=1 Сост=Done Бал=1
2013/02/04 17:25:05.971|       |QuikManager|235 Заявка 62514037/1981814003 Покупка Цена=161230 Объем=1 Сост=Active Бал=1
2013/02/04 17:25:06.171|       |QuikManager|235 Заявка 62514037/1981814003 Покупка Цена=161230 Объем=1 Сост=Done Бал=1
2013/02/04 17:25:06.172|       |QuikManager|200 Заявка 62514038/1981814030 Покупка Цена=161230 Объем=1 Сост=Done Бал=0
2013/02/04 17:25:06.250|       |QuikManager|251 Сделка 04.02.2013 17:25:06 82246193 161230 1 от заявки 62514038/1981814030 Покупка Цена=161230 Объем=1 Сост=Done Бал=0
2013/02/04 17:25:06.250|       |QuikManager|200 Заявка 62514038/1981814030 Покупка Цена=161230 Объем=1 Сост=Done Бал=0
2013/02/04 17:25:06.326|Error  |QuikManager|Ошибка регистрации заявки: System.InvalidOperationException: Сервер для транзакции 'ACTION=MOVE_ORDERS; CLASSCODE=SPBFUT; SECCODE=RIH3; MODE=0; FIRST_ORDER_NUMBER=1981814030; FIRST_ORDER_NEW_PRICE=161230; FIRST_ORDER_NEW_QUANTITY=0; TRANS_ID=62514039;' вернул неправильное сообщение 'Не найдена активная заявка для перестановки' по передвинутым заявкам.

Позиция закрывается, а вот в процессе - косяки.