Частичное исполнение лимитной заявки

Частичное исполнение лимитной заявки
Atom
6/19/2017
Knup


Добрый день,

Подскажите, пожалуйста, как на S# корректно реализовать следующий алгоритм:

  1. Выставляем лимитную заявку на покупку
  2. При каждом частичном исполнении заявки выставляется тейк-профит в виде противоположной лимитной заявки. Тейк-профит должен быть всегда один, т.е. при каждом новом частичном исполнении заявки старый тейк-профит отменяется, новый же с сальдированным большим объёмом - выставляется. И так, пока заявка на покупку полностью не исполнится. Т.е. в итоге после исполнения лимитной заявки на покупку из n лотов должна быть одна противоположная заявка на продажу (тейк-профит) тоже из n лотов.

Делаю примерно так через WhenNewTrade:


...
_orderOpen.WhenNewTrade(Connector)
                .Do(o =>
                {
                    // фиксируем набранную позицию
                    _summVolumeOpen += o.Trade.Volume;
                    _summPriceVolumeOpen += _orderdata.OpenPrice * o.Trade.Volume;

                    // Создать заявку тейк-профит по набранной позиции
                    CreateTakeProfitOrderSell(_summVolumeOpen);
                })
                .Apply(this);
...

        private void CreateTakeProfitOrderSell(decimal curvol)
        {
            lock (_syncObjSafe)
            {
                if (_orderClose != null && _orderClose.State == OrderStates.Active)
                    CancelOrder(_orderClose);
                
                _orderClose = this.SellAtLimit(_orderdata.ClosePrice, curvol);
                
                ...

                RegisterOrder(_orderClose);
            }
        }

Проблема в том, что при исполнении лимитной заявки по частям WhenNewTrade вызывается быстро несколько раз так, что заявка тейк-профит _orderClose не успевает принять состояние Active и поэтому не отменяется. WhenPartiallyMatched, как я понял, тоже не решает проблему - это правило вызывается столько же раз, сколько и WhenNewTrade. В общем, в результате вместо одного тейк-профита у меня выставляется сразу несколько, что неправильно (все заявки кроме последней должны быть отменёнными).



Support

Avatar
Date: 6/19/2017
Reply


  1. Лучше использовать два события WhenPartyallyMatched и WhenNewTrade, потому что WhenPartyallyMatched обычно приходит раньше WhenNewTrade... В WhenNewTrade мы только проверяем была ли выполнена обработка в WhenPartyallyMatched.. 2. Вам не нужно полностью полагаться на статусы и события зяявки, а использовать собственные флаги для контроля за состоянием...которые проверить
Thanks:

Knup

Avatar
Date: 6/19/2017
Reply


А как это реализуется на практике через собственные флаги? Я имею ввиду как дождаться, того, что выставленная заявка станет активной, чтобы к ней можно было применить CancelOrder ? Просто не совсем очевидно.

Thanks:

Support

Avatar
Date: 6/20/2017
Reply


  1. Есть событие WhenRegistered,,, В этот момент заявка имеет статус Act ive. 2, Могу рекомендовать следующий подход.. Для каждого "логического" типа заявок (вход, тейк, стоп и т.п.) создается метод, в котором собраны все события для данной заявки... В событиях заявки реализуется необходимая логика.. Можно иметь шаблон такого метода и использовать его в разных стратегиях. Ниже показан пример..
   private void TakeOrderProcessing(Order order)
    {

        if (_isStopping)
            return;

        order.Comment = "Take";

        try
        {

            if (this.Root.SessionState != eSessionState.ActiveSession)
                throw new Exception("Не могу выставить заявку - сессия неактивна!");

            order.WhenChanged(this.Connector).Do(o =>
            {
                this.AddDebugLog("{0} заявка изменена. {1}", o.Comment, o);

                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(ord => ord.TransactionId == o.TransactionId));
                if (bl != null)
                    Take.CanChange = bl.Take.Orders.Any(ord => ord.State == OrderStates.Active);

            }).Apply(this);

            order.WhenRegistered(this.Connector).Do(o =>
            {

                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(ord => ord.TransactionId == o.TransactionId));

                if (bl != null)
                    Take.CanChange = bl.Take.Orders.Any(ord => ord.State == OrderStates.Active);


                this.AddDebugLog("{0} заявка зарегистрирована. {1}", o.Comment, o);
            }).Apply(this).Once();

            var orderWhenRegisterFailed = order.WhenRegisterFailed(this.Connector);

            orderWhenRegisterFailed.Do(of =>
            {
                this.AddWarningLog("Ошибка регистрации {0} заявки. {1}. {2}", of.Order.Comment, of.Order, of.Error.Message);
                this.Rules.RemoveRulesByToken(orderWhenRegisterFailed.Token, orderWhenRegisterFailed);
            }).Apply(this);

            var orderWhenCanceled = order.WhenCanceled(this.Connector);

            orderWhenCanceled.Do(o =>
            {
                this.AddInfoLog("Отменена {0} заявка. {1}", o.Comment, o);

                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(ord => ord.TransactionId == o.TransactionId));

                if (bl != null)
                {

                    Take.CanChange = bl.Take.Orders.Any(ord => ord.State == OrderStates.Active);

                    if (AlgorithmStatus == AlgorithmStatuses.SendCancelTake)
                    {


                        if (bl.Take.Orders.All(t => t.State != OrderStates.Active))
                        {
                            var vol = Volume - bl.Take.Orders.Sum(t => t.Volume - t.Balance);

                            var newTake = this.CreateOrder(Direction.Invert(), _newTakePrice, vol);

                            bl.Take.ActivateLevel = _newTakePrice;
                            bl.Take.Orders.Add(newTake);

                            this.AddInfoLog("Изменение тейк-заявки. Послылаем команда на выставление новой тейк-заявки. Новая цена {0}", _newTakePrice);

                            TakeOrderProcessing(newTake);
                        }

                    }
                }
                else
                    this.AddWarningLog("Не найден TakeStopBlock");

                UpdateInfo();

                this.Rules.RemoveRulesByToken(orderWhenCanceled.Token, orderWhenCanceled);
            }).Apply(this);

            var orderWhenMatched = order.WhenMatched(this.Connector);
            orderWhenMatched.Do(o =>
            {
                this.AddInfoLog("Полное исполнение {0} заявки. {1}", o.Comment, o);

                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(ord => ord.TransactionId == o.TransactionId));

                if (bl != null)
                {
                    Take.CanChange = bl.Take.Orders.Any(ord => ord.State == OrderStates.Active);
                    var enterVolume = bl.Enter.Orders.Where(ord => ord.State == OrderStates.Done).Sum(ord => ord.Volume - ord.Balance);
                    var takeVolume = bl.Take.Orders.Where(ord => ord.State == OrderStates.Done).Sum(ord => ord.Volume - ord.Balance);

                    if (enterVolume == takeVolume)
                        AlgorithmStatus = AlgorithmStatuses.Take;

                }
                else
                    this.AddWarningLog("Не найден TakeStopBlock");

                UpdateInfo();

                this.Rules.RemoveRulesByToken(orderWhenMatched.Token, orderWhenMatched);
            }).Apply(this);

            order.WhenPartiallyMatched(this.Connector).Do(o =>
            {
                if (o.IsMatchedPartially())
                {
                    this.AddInfoLog("Частичное исполнение {0} заявки. {1}", o.Comment, o);
                }
            }).Apply(this);

            order.WhenNewTrade(this.Connector).Do(trade =>
            {
                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(o => o.TransactionId == trade.Order.TransactionId));
                if (bl != null)
                {
                    if (bl.Take.Trades.All(t => t.Trade.Id != trade.Trade.Id))
                        bl.Take.Trades.Add(trade);
                }

                UpdateInfo();

            }).Apply(this);

            AlgorithmStatus = AlgorithmStatuses.SendTake;

            this.RegisterOrder(order);

            this.AddInfoLog("Послана {0} заявка. Price: {0}", order.Comment, order.Price);
        }
        catch (Exception ex)
        {
            this.AddErrorLog(string.Format("{0} / {1}", ex.Message, ex.StackTrace));
        }

    } 
Thanks: Knup


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

loading
clippy